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.

    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. :thumbup:

    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.



    Man kann auch Accounts/%s.ini benutzen, bleibt jedem selbst überlassen. ^^


    Edit: Zitat eingefügt.


    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

    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.


    Guck 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... ^^

    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:



    Download ist im Anhang.
    Source: Ampaste.net


    Viel 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.

    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 :thumbup:


    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? :P

    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.