ich weiß sehr wohl was ein Tutorial ist und wie man ein Tutorial schreibt, der code war auch einfach nur hingeklatscht für die erfahrenen benutzer, ich werde es nachher nochmal erneuern und es besser erklären
Beiträge von .#~Momo
-
-
Das meinte ich nicht, ich meinte eigentlich nur warum ihr die variablen extrem abkürzt wie zb. i, j, a, b.
warum schreibt ihr die nicht komplett aus zb index oder pid ist doch besser wie immer nur anfangsbuchstaben zu benutzen
Zumal halte ich mich für nichts besseres so ein schwachsinn, ich habe selber keine ahnung vom programmieren, ich kanns auch nur flüchtlig.
-
Wie sagt man doch gleich nochmal? Kritik ist gut und nicht negativ
Ich werde es Heute Abend mal überarbeiten, da ich z.Z wenig Zeit habe.
-
Hast recht, Funktioniert nur auf Windows, ich selber habe 0 ahnung von Linux, es ist auch nur eine übersetzung von GetPrivateProfileString / WritePrivateProfileString, jedoch für die anfänger nützlich die den Server evtl auf Windows Hosten.
Man kann in Pawn auch einfach ein eigenes INI system scripten, was die meisten wohl nicht können.
-
keine lust hast, es zu erklären hast du auch keine Lust ein Tutorial zu schreiben, und somit hättest du dir das sparen
Was gibt es auch großartig zu erklären? Jeder Scripter der auch nur ein bisschen hirn hat wird den Code ja sofort beim ersten mal verstehen, wer es nicht versteht ist zu dumm und brauch erst garnicht anfangen zu Scripten, sorry aber das ist nun mal meine meinung.
Ich übe mal Kritik. Es sieht nicht nur schwer aus, für Anfänger ist es das auch - daher könntest du ruhig das ganze erklären.
Dann sind mir noch so zwei Dinge aufgefallen, einmal
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
müsste das nicht "Accounts/%s.ini heißen? Ansonsten hast du noch einen Rechtschreibfehler gemacht
LgMan kann auch Accounts/%s.ini benutzen, bleibt jedem selbst überlassen.
Edit: Zitat eingefügt.
-
Eine kleine und kurze frage:
Wie kann ich eine for-Schleife abbrechen ?
Ich denke mal "break;", in der Wiki werde ich auch nicht sehr Schlau hehe..Genauer gesagt:
for (new i = 0; i < 10; i++)
{
printf("%d", i);
if (i == 5)
{
break;
}
}
Würde das nur "if(i==5)" abbrechen oder die for-Schleife ?Warum macht ihr eigentlich immer for(new i = 0; i != 100; i++) der nächste der kommt weiß nicht wofür das i eigentlich ist... wenn ich durch alle spieler loope, dann sieht das so aus: for(new pid = 0; pid != MAX_PLAYERS; pid++) somit weiß ja jeder sofort für das PID (Player ID) ist ^^, ich seh leute die benutzen manchmal i, j, a, b... hallo? nach einer zeit wisst ihr selber nicht mehr wofür das ganze ist ^^.
-
Ob SpawnPlayer oben oder unten ist, das macht kein unterschied, ich selber erstelle lieber erst alle files und lass den spieler dann spawnen.
Edit: aaaaaaaah, hab dein Code nicht durchgelesen, klar so geht es auch, aber dann hast du den Wert nicht in dem Spieler Enum, es gibt 2 wege, hier ein bsp. mit SetPlayerScore:
New Score = LoadInteger("Account", "Score", "0", "Accounts\\Momo5000.ini");
SetPlayerScore(playerid, Score);Oder einfach so:
SetPlayerScore(playerid, LoadInteger("Account", "Score", "0", "Accounts\\Momo5000.ini");Der unterschied ist der, falls du ein Spieler Enum hast, wird der Wert dort nicht gespeichert, sondern du setzt den Spieler Score sofort auf den Wert, den er aus der INI bekommt.
Weg 1 ist somit schon etwas besser, MFG
-
So hier die neue Version von meinem Tutorial wie ihr ein einfaches aber sicheres Login system erstellt.
Wir brauchen als ersten das INI Plugin für SAMP was hier hier findet: >>> KLICK <<<
Sobald wir das Plugin installiert haben, erstellen wir ein neuen Gamemode und includen die ini include datei mit
#include <a_samp_ini>
Als nächstes erstellen wir ein Spieler enum, das sollte so aussehen:
enum PInfo
{
pPassword[16],
pLevel,
pMoney,
Float:pX,
Float:pY,
Float:pZ,
pIsPlayerLoggedIn
}direkt unter unserm Spieler enum erstellen wir ein array + ein globalen string array wie folgt:
new PlayerInfo[MAX_PLAYERS][PInfo];
new str[128];als nächstes dirigieren wir zu dem "OnPlayerRequestSpawn" Callback und modifizieren es ein wenig, bis es so aussieht:
public OnPlayerRequestSpawn(playerid)
{
if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 0)
{
SendClientMessage(playerid, 0xAAAAAAAA, "Du kannst nicht unangemeldet spawnen.");
return 0;
}
return 1;
}Erklärung: if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 0) Checkt ob der Spieler eingeloggt ist, ist der Spieler nicht eingeloggt Sendet er eine Chat nachricht und returnt 0, der return 0 verhindert das der Spieler spawnen kann solang die pIsPlayerLoggedIn variable 0 ist.
Und nun suchen wir das OnPlayerSpawn Callback und fügen diesen Code ein:
public OnPlayerSpawn(playerid)
{
if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 0)
{
// Die ist notwendig da man dank m0d s0beit unangemeldet spawnen kann. (Fragt nicht wie es geht, ich werde es euch nicht sagen.)
format(str, sizeof(str), "%s wurde gekickt da er unangemeldet gespawn ist", GetPlayerNameEx(playerid));
SendClientMessageToAll(COLOR_LIGHT_RED, str);
return 0;
}
return 1;
}
Dieser abschnitt ist notwendig da man DANK mod so*eit ohne sich einzuloggen spawnen kann. sollte der spieler also unangemeldet spawnen, so wird er vom server gekickt.Wir fügen nun folgenden Makro unter den Includes ein:
#define Command(%1) (!strcmp(cmdtext, %1, true, strlen(%1)) && strdel(cmdtext, 0, strlen(%1) + 1) == true)
Diesen Makro brauchen wir da er die benutzen bei OnPlayerCommandText vereinfacht, und wir gehen nun zu unseren OnPlayerCommandText callback und bearbeiten es.public OnPlayerCommandText(playerid, cmdtext[])
{
if(Command("/login"))
{
if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 1) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "Du bist schon angemeldet.");
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
if(!fexist(str)) return SendClientMessage(playerid, COLOR_YELLOW, "Account wurde nicht gefunden, du kannst dich nicht einloggen.");
new strPW[16];
if(sscanf(cmdtext, "s", strPW)) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "[SERVER] Benutzung: /login [Passwort]");
OnPlayerLogin(playerid, strPW);
return 1;
}
if(Command("/register"))
{
if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 1) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "Du bist schon angemeldet.");
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
if(fexist(str)) return SendClientMessage(playerid, COLOR_YELLOW, "Account wurde gefunden, du kannst dich nicht registrieren.");
new strPW[16];
if(sscanf(cmdtext, "s", strPW)) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "[SERVER] Benutzung: /register [Passwort]");
OnPlayerRegister(playerid, strPW);
return 1;
}
return 0;
}Die einzelnen Funktionen könnt ihr auf http://wiki.sa-mp.com sowieo http://wiki.sa-mp.de nachlesen, ich denke aber mal die dürften euch bekannt sein.
Falls ihr nicht wisst was sscanf ist: hier die erklärung: >>> Klick <<<Wir erstellen nun ganz unten im Script ein neuen Callback namens:
public OnPlayerRegister(playerid, password[])
{
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
SaveString("Account", "Password", password, str);
SaveInteger("Account", "Level", random(10), str);
SaveInteger("Account", "Money", random(5000), str);
SaveFloat("Account", "PosX", 1603.928466, str);
SaveFloat("Account", "PosY", 1820.088500, str);
SaveFloat("Account", "PosZ", 10.828001, str);
SendClientMessage(playerid, COLOR_LIGHT_BLUE, "Du hast dich erfolgreich registriert, du wirst nun automatich eingeloggt.");
OnPlayerLogin(playerid, password);
return 1;
}Dieser Callback wird aufgerufen sobald ein Spieler /register [Passwort] eingibt. Erklärungen zu den SaveFloat,Integer,String funktionen findet ihr in meinem Plugin Thread.
Wir brauchen nun noch ein Login Callback:
public OnPlayerLogin(playerid, password[])
{
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
LoadString("Account", "Password", "0", PlayerInfo[playerid][pPassword], str);
if(!strcmp(PlayerInfo[playerid][pPassword], password, false))
{PlayerInfo[playerid][pLevel] = LoadInteger("Account", "Level", "0", str);
PlayerInfo[playerid][pMoney] = LoadInteger("Account", "Money", "0", str);
PlayerInfo[playerid][pX] = LoadFloat("Account", "PosX", "0.0", str);
PlayerInfo[playerid][pY] = LoadFloat("Account", "PosY", "0.0", str);
PlayerInfo[playerid][pZ] = LoadFloat("Account", "PosZ", "0.0", str);
PlayerInfo[playerid][pIsPlayerLoggedIn] = 1;
SpawnPlayer(playerid);
SetPlayerScore(playerid, PlayerInfo[playerid][pLevel));
GivePlayerMoney(playerid, PlayerInfo[playerid][pMoney]);
SetPlayerPos(playerid, PlayerInfo[playerid][pX], PlayerInfo[playerid][pY], PlayerInfo[playerid][pZ);
format(str, sizeof(str), "Willkommen zurück %s", GetPlayerNameEx(playerid));
SendClientMessage(playerid, COLOR_WHITE, str);
}else{
SendClientMessage(playerid, COLOR_LIGHT_RED, "Das Passwort war nocht korrekt.");
Kick(playerid);
}
return 1;
}Erklärung: OnPlayerLogin lädt das Passwort als erstes aus der ini Datei, sollte das ausgelesene Passwort nicht mit dem eingegeben Passwort übereinstimmen, wird der User gekickt. sollte das Passwort doch übereinstimmen werden alle gespeicherten Daten auf den Splieler Enum zugewiesen.
Jetzt brauchen wir noch ein Callback was alles Speichert sobald der Spieler den Server verlässt:
public OnPlayerSave(playerid)
{
format(str, sizeof(str), "Accounts\\%s.ini", GetPlayerNameEx(playerid));
SaveString("Account", "Password", PlayerInfo[playerid][pPassword], str);
SaveInteger("Account", "Level", PlayerInfo[playerid][pLevel], str);
SaveInteger("Account", "Money", PlayerInfo[playerid][pMoney], str);
new Float:X, Float:Y, Float:Z
GetPlayerPos(playerid, X, Y, Z);
SaveFloat("Account", "PosX", X, str);
SaveFloat("Account", "PosY", Y, str);
SaveFloat("Account", "PosZ", Z, str);
return 1;
}Wir dirigieren nun zu dem OnPlayerDisconnect callback und bearbeiten es:
public OnPlayerDisconnect(playerid, reason)
{
if(PlayerInfo[playerid][pIsPlayerLoggedIn] == 1)
{
OnPlayerSave(playerid);
PlayerInfo[playerid][pPassword] = 0;
PlayerInfo[playerid][pLevel] = 0;
PlayerInfo[playerid][pMoney] = 0;
PlayerInfo[playerid][pX] = 0.0;
PlayerInfo[playerid][pY] = 0.0;
PlayerInfo[playerid][pZ] = 0.0;
PlayerInfo[playerid][pIsPlayerLoggedIn] = 0;
}
return 1;
}Erklärung: Sollte ein Spieler eingeloggt sein, so werden alle Daten aus dem Spieler Enum gespeichert sobald der Spieler den Server verlässt, dabei spielt die Art des Disconnects keine rolle.
Zur Sicherheit um Bugs zu vermeiden werden nach dem Speichern die gesammten Variablen aus dem Spieler Enum auf 0 gesetzt.Zu guter letzt müssten wir noch unsere GetPlayerNameEx und die sscanf Funktionen erstellen:
stock GetPlayerNameEx(playerid)
{
new _NAME[MAX_PLAYER_NAME];
GetPlayerName(playerid, _NAME, sizeof(_NAME));
return _NAME;
}Natührlich müssen wir unsere neuen 3 Callbacks auch forwarden, dazu schreiben wir folgendes einfach unter den Includes:
forward OnPlayerLogin(playerid, password[]);
forward OnPlayerRegister(playerid, password[]);
forward OnPlayerSave(playerid);Das war es eigentlich auch schon, hoffe jetzt gibt es nicht mehr so viel zu meckern wegen meinem Tutorial.
Hier der Link zum vollen Code: >>> Klick <<<
Edit: Tutorial wurde komplett überarbeitet.
Edit: Rechtschreibung -
So kleines update, das include wurde modifiziert, es erhält 4 neue Funktionen die das Speicher/Laden von Integer und Floats vereinfacht.
-
Moin, hab mal n bisschen in GTA rumgestöbert und siehe da ich habe einen schnelleren weg gefunden CMD's vom Client zum Server zu senden. (Normale Text funktionieren bis jetzt noch nicht)
Ihr fragt euch sicherlich jetzt Das ist schneller als ein keybinder? niemals, ich erkläre euch kurz die wege in einem simplen pseudo code:
Keybinder Weg:
1) Drückt T und öffnet den Chat
2) Gibt einen Befehl so schnell ein das man ihn nicht sieht (bsp: /buyweapon AK47)
3) der Befehl wird in einem register gespeichert
4) samp.dll sendet den string an der Server über einer Funktion die in der samp.dll aufgerufen wird zb: "SendCommand(char* cmd)"Mein Weg ist viel schneller:
* In Unsere DLL rufen wir die Funktion so auf: "SendChat("/buyweapon AK47")";
1) Funktion Kopiert den string in einem register.
2) callt die SendCommand funktion und sendet den Befehl aus dem register sofort zum server.Klingt einfach oder?
PS: Das hier ist nichts für anfänger, fragt mich nicht wie benutze ich diesen code, ich werde keine hilfestellung dafür geben. C++ Basic solltet ihr können.
// Ihr solltet die SAMP base Adresse bekommen, das geht so:
DWORD adr_SAMP = GetModuleHandle(0, "samp.dll");
#define FUNC_SENDCMD 0x3CAC0
void SendChat(char *msg)
{
if(msg == NULL)
return;
if(msg[0] == '/')
{
int func = adr_SAMP + FUNC_SENDCMD;
__asm push msg
__asm call func}
else
{
//int func = adr_SAMP + FUNC_SAY;
//__asm mov ebx, dword ptr[LokalerSpieler?]
//__asm push msg
//__asm call func
//__asm pop ebx}
}// Wenn wir jetzt SendChat("/enter") aufrufen sendet der Client den Befehl sofort zum Server, ohne das er etwas im Chat eingibt.
SendChat("/enter"); // So rufen wir die Funktion auf.Edit: Rechtschreibung verbessert.
Edit 2: Offsets eingefügt, habs vergessen. -
Wie füge ich Filterscript ein dass diese auch auf dem server funktionieren? Sorry ist mein 2. Tag an dem ich als Scripter lerne
/edit:
Was bedeutet dieser Error?
C:.....(22837) : error 001: expected token: ";", but found "-identifier-"
C:\....(26207) : warning 235: public function lacks forward declaration (symbol "OnPlayerPrivmsg")
Pawn compiler 3.2.3664 Copyright (c) 1997-2006, ITB CompuPhaseGuck genau 1 zeile über der Fehlermeldung, dort ist meistens der Fehler, hast wohl vergessen ein ; zu setzen.
Was zum henker sucht der OnPlayerPrivmsg Callback bei dir im script? den gibt es doch seit 0.3 nicht mehr. -
Moin, ich hab problem mit meinem cmd, er returnt ihrgendwie immer 0, ich bekomme immer UNKOWN COMMAND ingame... und ja ich return 1 als wert hoffe mir kann wer helfen...
Bei: PlayerInfo[playerid][pVehicleID] = CreateVehicleEx(vmodel, 0, 0, 5, 0, random(126), random(126), -1); returnt er 0, soviel hab ich rausgefunden.
if(Command("/buycar"))
{
#pragma unused params
if(PlayerInfo[playerid][pVehicleModel] != 0) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "Du besitzt bereits ein Auto.");
if(!IsPlayerInAnyVehicle(playerid)) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "Du kannst diesen Befehl nur in einem Fahrzeug ausführen.");
new vehid = GetPlayerVehicleID(playerid);
for(new index = 0; index < sizeof(CarHouseVehicle); index++)
{
if(vehid == CarHouseVehicle[index])
{
if(GetPlayerMoney(playerid) < CarSellVehicleArray[index][CHPrice]) return SendClientMessage(playerid, COLOR_LIGHT_WHITE, "Du hast nicht genug Geld."); // Todo: Richtiges Money System
new vmodel = GetVehicleModel(CarSellVehicleArray[index][CHModelID]);
PlayerInfo[playerid][pVehicleModel] = vmodel;
PlayerInfo[playerid][pVehicleID] = CreateVehicleEx(vmodel, 0, 0, 5, 0, random(126), random(126), -1);
// Der gibt hier ihrgendwie immer den wert: 0 zurück, somit bekomme ich ingame die "SERVER: UNKOWN COMMAND" nachricht.
SendClientMessage(playerid, COLOR_GREEN, "Dein Auto wurde gekauft!");
return 1;
}
}
return 1
}stock CreateVehicleEx(vehicletype, Float:X, Float:Y, Float:Z, Float:R, color1, color2, respawntime)
{
if(CreatedVehicles == MAX_VEHICLES)
{
printf("Es können keine weiteren Autos erstellt werden. (Limit: %i / %i)", CreatedVehicles, MAX_VEHICLES);
return INVALID_VEHICLE_ID;
}
new vehid = CreateVehicle(vehicletype, X, Y, Z+0.3, R, color1, color2, respawntime);
VehicleInfo[vehid][VehicleColor1] = color1;
VehicleInfo[vehid][VehicleColor2] = color2;
CreatedVehicles++;
printf("Es wurde ein Fahrzeug erstellt. (Limit: %i / %i)", CreatedVehicles, MAX_VEHICLES);
return vehid;
}/ Edit: Color tag entfernt, funzt nit.
// EDIT: Hab den übertäter ^^... new vmodel = GetVehicleModel(CarSellVehicleArray[index][CHModelID]); so sollte es aufjedenfall nicht sein eher so: new vmodel = CarSellVehicleArray[index][CHModelID];...
Nachts scripten ist nicht grade berauschend...
-
Wie wäre es mit
new ThePlayer[MAX_PLAYERS]; -
SA:MP - ini Plugin
Das
ini Plugin ist ein erweitertes Speicher und Lade Plugin, es ermöglicht
euch in Echtzeit bestimmte variable in einer datei zu Speichern, es ist
um einiges schneller wie DINI, aber ob es schneller als das RAW auslesen
von datein ist kann ich leider nicht sagen.Erklärung:
-
SaveString / SaveInteger / SaveFloat Parameter:
- lpSector - Erstellt einen neuen Sektor in der ini Datei
- lpVariable - Erstellt eine neue Variable damit wir unseren Wert später wieder finden
- lpString - Unser eigentlicher Wert (Kann als Float, Integer und String angegeben
werden *) der nach dem lpVariable gespeichert wird. - lpFileName - Der Pfad zur Datei
- Return True wenn alles beim Schpeichern geklappt hat, False wenn es ein Fehler gab.
-
LoadString / LoadInteger / LoadFloat Parameter:
- lpSector - Der Sektor in einer ini Datei
- lpVariable - Unsere Variable in der ini Datei
- lpDefault - Unser Default wert der als String zurückgegeben wird falls es nicht
möglich war den Variable-wert auszulesen (Standart: 0) - lpDest - Hier wird unser ausgelesener String gespeichert. (Wird nur bei
LoadString benötigt, LoadInteger und LoadFloat hat diesen Parameter
nicht, die geben den wert einfach zurück und er kann auf einer variable
zugewiesen werden. bsp: new Float:PosX = LoadFloat("Account", "PosX",
"0.0", "Accounts\\Momo5000.ini") - lpFileName - Der Pfad zur ini-Datei
- Return True wenn alles beim Laden geklappt hat, False wenn es ein Fehler gab
-
StringToInteger
- Konvertiert einen String zu einem Integer Wert
- Returnt den Wert als Integer
-
StringToFloat
- Konvertiert einen String zu einem Float Wert
- Returnt Wert als Float
-
IntegerToString
- Konvertiert eine Integer Value zu einem String, Dieser Vorgang muss beim Speichert gemacht werden.
- Returnt Den Wert als String
-
FloatToString
- Konvertiert einen Float Wert zu einem String, Dieser Vorgang muss beim Speichern gemacht werden.
- Returnt den Float Wert als String
Die erstellte ini-Datei sieht dann wiefolgt aus:
Code
Alles anzeigen[lpSector] lpVariable=lpString so kann es zb. in einer datei mit mehreren Sektoren aussehen, aber Achtung!!! Alle werte sind Strings, wir müssen diese Strings erst beim Laden zu einem Float oder Integer Konvertieren, sollte der Wert ein String bleiben wie zb. das Passwort, müssen wir nicht Konvertieren. [Account] Passwort=test123 Geld=55555 Handynr=019583 [Auto] VehicleID=522 PosX=123.000 PosY=123.000 PosZ=123.000
Download ist im Anhang.
Source: Ampaste.netViel Spaß damit
Edit: 4 Neue Funktionen: SaveInteger, SaveFloat, LoadInteger und LoadFloat.
Edit: LoadString bereich geändert.
Edit: Text hat sich von alleine verschoben? habs aufjedenfall behoben. -
SaveString / SaveInteger / SaveFloat Parameter:
-
Fast Player Commands (FPC)
FPC ist eine art Makro die es ermöglicht Befehle schneller auszuführen und dank sscanf können wir sogar die einzelnen Parameter auslesen.
Es gibt nicht viel dabei zu erklären, es ist ein einfaches Makro das viel viel kürzer ist wie zb. dcmd und ihr müsst nicht kein extra dcmd_ thread hinzufügen, ihr könnt alles in dem Callback erledigen.Das ganze ist Resourcen sparender als strtok, strrest, dcmd_...
Die groß und kleinschreibung wird für jedem befehl nicht beachtet, ihr könnt also alle befehle mit CAPSLOCK oder ganz klein schreiben, es macht kein unterschied wenn ihr diesen Befehl ingame eingibt.
Ihr benötigt lediglich die sscanf funktion von Y_Less.In eurem Filterscript / Gamemode könnte dass ganze dann so aussehen:
public OnPlayerCommandText(playerid, cmdtext[])
{
if(Command("/KickEx"))
{
new pid, kickreason[128];
if(sscanf(cmdtext, "uz", pid, kickreason)) return SendClientMessage(playerid, COLOR_WHITE, "[Benutzung] /KickEx [Name/ID] [Reason]");
if(!IsPlayerConnected(pid)) return SendClientMessage(playerid, COLOR_WHITE, "Spieler wurde nicht gefunden.");
if(!strlen(kickreason)) kickreason[0] = '-'
format(str, sizeof(str), "Du wurdest vom server gekickt, grund: %s", kickreason);
SendClientMessage(pid, COLOR_RED, str);
Kick(pid);
return 1;
}
return 0;
}Die einzelnen befehle müssen wohl nicht erklärt werden, die dürften euch ja klar sein ;).
Wer die sscanf befehle nicht kennt:
- u - Hiermit könnt ihr die Spielerid raufinden *
- c - Ein Char
- d, i - Ein Zahlenwert KEIN FLOAT!
- h, x - Ein Hex-wert
- f - Ein Floatwert
- s - Ein einzelner String
- z - Der Restliche string **
- pX - An additional delimiter where X is another character. // Keine ahnung
- '' - Encloses a litteral string to locate. // Keine ahnung
* Wenn ihr zb ein /kick befehl habt und ihr wollt den spieler mit /kick Spieler kicken, könnt ihr ein teil des namens angeben, falls mehrer den gleichen eingegeben part haben wird INVALID_PLAYER_ID (0xFFFF) zurückgegeben, angemommen der User heißt Momo5000 (ID: 0) und wollen ihn kicken, wir haben nun 3 varianten zu verfügung:
/kick Momo
/kick Momo5000
/kick 0** z sollte immer am ende eingesetzt werden da es alle restlichen parameter als string zurück gibt.
Wie könnt ihr FPC benutzen? ganz einfach, ihr fügt diesen Define unter den includes ein.
#define Command(%1) (!strcmp(cmdtext, %1, true, strlen(%1)) && strdel(cmdtext, 0, strlen(%1) + 1) == true)Und zu guter letzt fügt ihr den Sscanf stock von Y_Less ganz untem im script ein:
/*
* @Function: sscanf(string[], format[], {Float,_}:...)
* @Info: Used to get the command params
*/stock sscanf(string[], format[], {Float,_}:...)
{
#if defined isnull
if (isnull(string))
#else
if (string[0] == 0 || (string[0] == 1 && string[1] == 0))
#endif
{
return format[0];
}
#pragma tabsize 4
new
formatPos = 0,
stringPos = 0,
paramPos = 2,
paramCount = numargs(),
delim = ' ';
while (string[stringPos] && string[stringPos] <= ' ')
{
stringPos++;
}
while (paramPos < paramCount && string[stringPos])
{
switch (format[formatPos++])
{
case '\0':
{
return 0;
}
case 'i', 'd':
{
new
neg = 1,
num = 0,
ch = string[stringPos];
if (ch == '-')
{
neg = -1;
ch = string[++stringPos];
}
do
{
stringPos++;
if ('0' <= ch <= '9')
{
num = (num * 10) + (ch - '0');
}
else
{
return -1;
}
}
while ((ch = string[stringPos]) > ' ' && ch != delim);
setarg(paramPos, 0, num * neg);
}
case 'h', 'x':
{
new
num = 0,
ch = string[stringPos];
do
{
stringPos++;
switch (ch)
{
case 'x', 'X':
{
num = 0;
continue;
}
case '0' .. '9':
{
num = (num << 4) | (ch - '0');
}
case 'a' .. 'f':
{
num = (num << 4) | (ch - ('a' - 10));
}
case 'A' .. 'F':
{
num = (num << 4) | (ch - ('A' - 10));
}
default:
{
return -1;
}
}
}
while ((ch = string[stringPos]) > ' ' && ch != delim);
setarg(paramPos, 0, num);
}
case 'c':
{
setarg(paramPos, 0, string[stringPos++]);
}
case 'f':
{
new changestr[16], changepos = 0, strpos = stringPos;
while(changepos < 16 && string[strpos] && string[strpos] != delim)
{
changestr[changepos++] = string[strpos++];
}
changestr[changepos] = '\0';
setarg(paramPos,0,_:floatstr(changestr));
}
case 'p':
{
delim = format[formatPos++];
continue;
}
case '\'':
{
new
end = formatPos - 1,
ch;
while ((ch = format[++end]) && ch != '\'') {}
if (!ch)
{
return -1;
}
format[end] = '\0';
if ((ch = strfind(string, format[formatPos], false, stringPos)) == -1)
{
if (format[end + 1])
{
return -1;
}
return 0;
}
format[end] = '\'';
stringPos = ch + (end - formatPos);
formatPos = end + 1;
}
case 'u':
{
new
end = stringPos - 1,
id = 0,
bool:num = true,
ch;
while ((ch = string[++end]) && ch != delim)
{
if (num)
{
if ('0' <= ch <= '9')
{
id = (id * 10) + (ch - '0');
}
else
{
num = false;
}
}
}
if (num && IsPlayerConnected(id))
{
setarg(paramPos, 0, id);
}
else
{
#if !defined foreach
#define foreach(%1,%2) for (new %2 = 0; %2 < MAX_PLAYERS; %2++) if (IsPlayerConnected(%2))
#define __SSCANF_FOREACH__
#endif
string[end] = '\0';
num = false;
new
name[MAX_PLAYER_NAME];
id = end - stringPos;
foreach (Player, playerid)
{
GetPlayerName(playerid, name, sizeof (name));
if (!strcmp(name, string[stringPos], true, id))
{
setarg(paramPos, 0, playerid);
num = true;
break;
}
}
if (!num)
{
setarg(paramPos, 0, INVALID_PLAYER_ID);
}
string[end] = ch;
#if defined __SSCANF_FOREACH__
#undef foreach
#undef __SSCANF_FOREACH__
#endif
}
stringPos = end;
}
case 's', 'z':
{
new
i = 0,
ch;
if (format[formatPos])
{
while ((ch = string[stringPos++]) && ch != delim)
{
setarg(paramPos, i++, ch);
}
if (!i)
{
return -1;
}
}
else
{
while ((ch = string[stringPos++]))
{
setarg(paramPos, i++, ch);
}
}
stringPos--;
setarg(paramPos, i, '\0');
}
default:
{
continue;
}
}
while (string[stringPos] && string[stringPos] != delim && string[stringPos] > ' ')
{
stringPos++;
}
while (string[stringPos] && (string[stringPos] == delim || string[stringPos] <= ' '))
{
stringPos++;
}
paramPos++;
}
do
{
if ((delim = format[formatPos++]) > ' ')
{
if (delim == '\'')
{
while ((delim = format[formatPos++]) && delim != '\'') {}
}
else if (delim != 'z')
{
return delim;
}
}
}
while (delim > ' ');
return 0;
}Bugs könnt ihr gerne reporten, ich versuche diese dann zu beheben, so viel spaß beim scripten nun
Edit: Sinnloses Include entfernt.
-
Starte dein PAWN-Editor mal als Administrator und setz die pawncc.exe auf dem Windows XP SP3 kompalitätsmodus, dürfte eigentlich helfen falls nicht geh zu sa-mp.com lad die die aktuelleste server version runter und benutz den PAWN-Editor.
-
Du hättest auch einfach ein jmp setzen können.
Das wäre definitiv schneller als No Operation's durch zu führen.Hätte ich auch machen können ist eigentlich auch besser & schneller, mal gucken wann ich n update rausbring. danke
Nice gemacht, das errinert mich irrgendwie an alte Warrock Zeiten xD
Jaja, die guten alten Cheater nicht wahr?
-
Erstmal brauchst du Ollydbg, ist ein debugger.
Danach startest du den Server, Ollydbg und wählst den SA:MP server aus.
Klickst auf dem E in Ollydbg und wählst dort nochmals die Server.exe aus, da er immer die ntdll.dll anzeigt.
Ist dies getahn suchst du die einfach nur die [join] [connect] [disconnect] strings, diese pusht er in einem register, meistens 1-4 zeilen dadrunter ist der call für die printf funktion. zb:
PUSH EAX
PUSH EBP ; Hier ist unser string "[join] [Momo5000] 127.0.0.1"
MOV EBP...
CALL samp-ser.00497140 ; Hier wird die printf Funktion aufgerufen mit allem parametern aus den registern.Diese Adresse NOP'st du einfach. (NOP = 0x90 = No Operation (Keine Funktion) und voila, der [join] part ist geschichte.
PS: Das ist die Orginale addrese:
0049BB82 E8 B9B5FFFF CALL samp-ser.00497140 ; (E8 B9B5FFFF = 5 Bytes) Pro Offset (0049BB82) kann ein BYTE eingetragen werden, in olly sieht das nur ein bisschen anders aus, hat keinen sinn wenn ich das jetzt alles hier erkläre, ich glaube du/ihr versteht das meiste eh nicht hiervon)
Also müssten wir die Call Funktion NOP'en, fertig sieht das ganze dann so aus:0049BB82 90 NOP
0049BB83 90 NOP
0049BB84 90 NOP
0049BB85 90 NOP
0049BB86 90 NOP -
Bis jetzt ist dieß nicht der fall, falls es sich doch beweist das bei einem Update etwas anders ist könnte ich das ganze ja auf einem Signatur Scan bearbeiten, somit bekommt ihr bei JEDER SA:MP Server version den richtigen Offset, Wie gesagt, bei mir läuft alles ohne Probleme.
-
Ich werde mal nachfragen ob es erlaubt ist die printf anzeigen zu entfernen, werde die antwort dann hier im Forum posten.