Genau, du musst da kein handle neu deklarieren, du hast ja bereits eins, einfach das verwenden und übergeben ![]()
//Edit: Wenn es von Anfang an so heißt, dann nenn es um oder im Include ![]()
Genau, du musst da kein handle neu deklarieren, du hast ja bereits eins, einfach das verwenden und übergeben ![]()
//Edit: Wenn es von Anfang an so heißt, dann nenn es um oder im Include ![]()
Für die Kennzeichen/Telefonnummer würde ich einfach aufsteigende Zahlenfolge nutzen.
Das freut mich ![]()
Selbst ein Index ist wahrscheinlich overkill, wenn wir von 1000 Zahlen reden, aber gut.
Ja, das versuche ich dir ja gerade zu vermitteln.
Desweiteren kannst du in Pawn nicht mal dynamisch Speicher allozieren, also würde das so gar nicht gehen.
Schon gar nicht bei mehreren Pools.
(Ja Memory Plugin außen vor, dennoch hat man dann noch O(n) Zugriffe auf das Array)
Wenn man für die Zufallszahl einen Index erstellt, dann ist der Check auf Duplikate relativ schnell
Wie willst du das machen?
Du musst ja trotzdem alle vorherigen Zahlen durchgehen, dein Ansatz ist der naive Ansatz, wie oben erklärt.
Und wenn du einen Pool von 10.000 Zahlen hast, dann viel Spaß. Dein Array musst du ja auch irgendwie deklarieren und das bezieht auch Speicher.
da es sonst zu viel Speicher frisst.
Nah jede Zahl frisst nur 4 Bytes.
Also selbst wenn du einen Pool aus 1.000.000 Zahlen hast, frisst das ((1.000.000*4)/1024) = 3.906,25kB = ~4MB
Also das ist ein RIESEN Pool, ich wüsste nicht, wofür man so viele eindeutig zufällige Zahlen bräuchte und man benötigt nur 4MB, was jeder Rechner locker zur Verfügung hat, immerhin Reden wir hier nicht von RAM, sondern von Festplattenspeicher.
Und wenn ich überlege, dass heute sogar Handys Teil schon 1TB haben (Galaxy S10 mit 1TB Speicher) sollte das mit den 4MB kein Problem sein ![]()
Hallo Brotfische,
ich bin es mal wieder *nerv nerv* mit einem neuen Include.
Inspiriert durch diesen Thread: Kennzeichen System / Polizei KFZ Akte
Es geht, wie der Titel es schon vermuten lässt um "Eindeutig zufällige Zahlen".
Aber was heißt das eigentlich?
Nun, wenn man z.B. die Funktion random(n) verwendet, dann erhält man zufällige Zahlen im Intervall von [0, n-1]. (Also zum Beispiel random(100) gibt uns die Zahlen von 0-99.)
So, verwenden wir diese Funktion jetzt noch einmal, dann erhalten wir mit Pech, die Selbe Zahl wie zuvor.
Das ist natürlich ärgerlich, wenn man jetzt Telefonnummern vergibt oder eindeutige Kennzeichen haben will.
Naive Lösung?
Eine naive Lösung wäre jetzt, eine Zahl zu generieren und dann zu speichern.
Anschließend alle Nachfolgenden generierten Zahlen durch die, die schon erstellt wurden zu iterieren und zu schauen, ob es bereits schon so eine Zahl gibt.
Falls dem so ist, eine neue generieren. Ansonsten hat man ja seine eindeutige Zahl gefunden.
Problem: Der Aufwand wird immer größer (je mehr Zahlen man generiert hat) und das will man gerade im Laufenden betrieb natürlich nicht. (O(n^x) n=Wie viele Zahlen es bereits gibt. x=Wie viele Versuche benötigt werden, eine zu finden, die noch nich existiert.)
Meine Lösung?
Es ist einfacher, einen Zahlen Pool abzubilden.
Sprich man will eine zufällige Zahl von 0-1000, dann speichert man einfach alle 1000 Zahlen in einer Datenbank.
Anschließend wird einfach eine zufällig ausgewählt und aus der Liste gelöscht (das heißt, je mehr verwendet werden, desto kleiner wird der Speicherplatz bedarf.)
Das bedeutet, egal wie viele Zahlen man hat, es ist immer gleich schnell (O(1)).
Das Gute hier ist noch, dass man Multi-Threaded querys nutzen kann (nur in MySQL, nicht SQLite), dann wird das eh ausgelagert alles und beeinflusst 0 den Server.
MySQL VS SQLite
Ich habe 2 Versionen geschrieben.
Definitiv zu präferieren wäre hier die MySQL Version (aber für die, die kein MySQL haben und das benötigen, habe ich eine SQLite Version geschrieben).
MySQL
Wie oben schon erwähnt, können wir hier Multi-Threaded Querys nutzen und das Asynchron gestalten.
Somit läuft hier alles sehr schnell und glatt.
SQLite
Hier belastet das die Server-Laufzeit. (Gerade der Server-Start, wenn die Pools inistialisiert werden, kann das (je nach Größe der Pools) bis zu einer Minute dauern.)
Die spätere Generierung dauert ~0.5ms. Also nicht soo viel, aber doch schon etwas, gerade wenn man hintereinander mehrere Zahlen generiert.
Beispiele
MySQL Version
Zuerst kann man sich die Pools anlegen, aus denen man dann die zufälligen Zahlen generiert:
Anschließend (kann man auch während der Server Laufzeit machen, allerdings sollte man dann etwas Zeit einplanen, bis man eine weitere Zahl generiert, da das asynchron abläuft belastet es den Server nicht, dauert trotzdem etwas) kann man die Pools erstellen:
/*
Die Parameter sehen so aus:
CreateRandomPool(MySQL_Handle, POOL, MIN_VALUE, MAX_VALUE);
*/
CreateRandomPool(handle, NUMBER_PLATE_POOL, 0, 1000); //Erstellt einen Pool von 0-999
CreateRandomPool(handle, SMALL_EXAMPLE_POOL, 250, 500); //Erstellt einen Pool von 250-499
So und dann kann man eine zufällige Zahl anfangen zu generieren:
/*
Die Parameter sehen so aus: (Wenn man playerid nicht nutzen will, kann man einfach 0 reinschreiben, ist nicht notwendig anzugeben)
StartGenerateUniqueRandomNumber(playerid, MySQL_Handle, POOL, TOKEN);
*/
StartGenerateUniqueRandomNumber(playerid, handle, SMALL_EXAMPLE_POOL, 0);
Darauf hin, wird ein Callback aufgerufen:
public OnRandomNumberGenerated(playerid, const pool, const token, const status)
{
if(status == -1) return printf("Der Random Pool (%d), ist leer!",pool);
switch(pool)
{
case SMALL_EXAMPLE_POOL:
{
switch(token)
{
case 0:
{
printf("Zufällige Nummer %d wurde aus SMALL_EXAMPLE_POOL generiert.", status);
}
}
}
}
return 1;
}
Alles anzeigen
Der TOKEN, dient dazu, zu differenzieren von wo aus der Callback aufgerufen wurde.
Damit man dann wieder zu Funktionen leiten kann, wo der Code weiter gehen soll.
SQLite Version
Zuerst kann man sich die Pools anlegen, aus denen man dann die zufälligen Zahlen generiert:
Anschließend (Bitte nur in Callbacks beim Server-Start, wie OnGameModeInit. Nicht während der Server läuft.) kann man die Pools erstellen:
/*
Die Parameter sehen so aus:
CreateRandomPool(POOL, MIN_VALUE, MAX_VALUE);
*/
CreateRandomPool(NUMBER_PLATE_POOL, 0, 1000); //Erstellt einen Pool von 0-999
CreateRandomPool(SMALL_EXAMPLE_POOL, 250, 500); //Erstellt einen Pool von 250-499
Und dann kann man einfach direkt eine Zahl generieren lassen:
/*
Die Parameter sehen so aus:
UniqueRandom(POOL);
*/
new r = UniqueRandom(SMALL_EXAMPLE_POOL);
printf("Eindeutig zufällige Zahl (%d) wurde generiert.",r);
Allerdings hier sehen wir, im Gegensatz zu der MySQL Version, läuft das direkt im Code (ohne Callback).
Das hat den Nachteil, dass dieser Prozess ~0.5ms dauert und nicht asynchron verarbeitet werden kann.
Was passiert, wenn der Pool leer ist?
Dann ist der status wie schon gezeigt -1.
Allerdings, wenn beim Server Restart immer noch das CreateRandomPool steht, wird dieser automatisch wieder aufgefüllt.
Download
Wer die MySQL Version verwenden will, da ist die Neuste MySQL Version (R41) nötig!
MySQL - Version - Links:
SQLite - Version - Links:
Wichtige Hinweise / Mögliche Errors
Schlusswort
Ich hoffe das Include ist für den ein oder anderen nützlich.
Bei Fragen oder Anregungen, stehe ich euch wie immer gerne zur Verfügung.
Viel Spaß weiterhin beim skripten ![]()
Mit freundlichen Grüßen
Euer Kalle ![]()
Bekomme immer noch denselben MySQL Fehler
Es handelt sich um ein Query, wo ein WHERE Statement steht.
Einfach:
if( strcmp( key , "BanReason" , true ) == 0 ) format(PlayerInfo[playerid][pBanReason], 64, ini_Get( Data )); //oder wie lang auch immer der Reason so sein kann
if( strcmp( key , "BanAdmin" , true ) == 0 ) format(PlayerInfo[playerid][pBanAdmin], MAX_PLAYER_NAME, ini_Get( Data ));
//Edit: Natürlich dini_get...
Es gab doch dieses Timestamp?
...da ist ein Komma vor der ( ![]()
Also wann die erstellt werden nachdem das Biz geladen wurde.
Nein, das ist ja egal, das Interior selbst existiert ja unabhängig von den Bizzen.
Und wie soll ich die erstellen
Na ganz normal mit einer Schleife:
Müsste das dann unter Bizladen hin?
Ich verstehe die Frage nicht lol.
BizInts ist eine globale Variable und muss ganz noch oben.
if(bi==0) return SendClientMessage(playerid,-1,"Das Biz hat kein Interior");
Dann lösch diese Zeile.
Vermutlich, ist der 24/7 id 0.
for(new e;e<=sizeof Building;e++)
Das ist falsch.
Schreib das mal:
for(new e;e<sizeof Building;e++)
Und setz mal unter Zeile 14 ein return 1; sonst läuft der Code ja weiter ![]()
Er kommt nicht soweit, nicht mal zum ersten es wird nix geprintet wie immer der selbe Fehler
Dann tritt der Fehler früher auf, wenn er da nicht mal rein geht ![]()
Das bedeutet...mehr prints und zwar früher haha ![]()
Ich hab da am Ende ein Print aber soweit kommt der erst garnicht.
Wenn ich sage..alles voller prints setzen, meine ich sicher nicht nur einen print ![]()
Sondern...überall in jede Code ebene.
Also immer vor die { was setzen um eben zu schauen, was gemacht wird.
und der string ist egal, wichtiger wäre sowas wie printf("A: %d",b); und beim Nächsten "B: %d" usw...xD
Dann pack da mal überall prints rein und schaue, was genau aufgerufen wird und welchen Wert die wichtigen Variablen haben ![]()
Kann dir morgen mal so ein System schreiben, oder direkt eine Include und es generalisieren und mal releasen.
Gerade nur keine Zeit ![]()
also ich habe 8 Biz soll ich jetzt noch 6 Dummys hinterherschicken oder wie
muss ich das verstehen?
Richtig, so dass du gleichviele Ints, wie Bizzes hast.
Sonst wie gesagt, bau es dynamisch auf, dann kannste einfach prüfen, ob das Biz schon einen Interior hat oder nicht.
FindCarByPlate
Nur mal aus Spaß, dass kann zu einer gefährlichen Funktion werden ![]()
Im worst worst case, sind 500 Spieler online und alle 9999 Autos vergeben.
Und wir wollen blöderweise eins der letzten Autos checken.
Das macht 500*9999 String Abfragen, das sind 4.999.500 ![]()
Resultat: ~930ms
Das bedeutet, dein Server würde für ne Sekunde hängen, spammt man den Befehl, wäre er quasi unspielbar und down xD
Wenn, dann solltest du direkt mit Zahlen arbeiten
(mit Zahlen sind es immer noch ~200ms)
Der Witz aber ist, dass der Code nicht einmal funktioniert. Denn wenn der Spieler, dem das Kennzeichen gehört nicht online ist, kann das Auto nicht zugeordnet werden.
Es wäre hier viel effizienter einen einfachen MySQL Query zu nutzen, mit dem du prüfst, zu wem das eindeutige Kennzeichen gehört ![]()
10.000 eindeutig zufällige Zahlen effizient zu generieren, die sich nicht wiederholen, ist gar nicht mal so trivial in Pawn.
Mir fällt mir ein, entweder y_iterate zu nutzen, oder besser ein eigenes System zu entwickeln.
Sprich du hast eine Datenbank mit 10.000 Einträgen. Dann wählst du random(n) (wo n die Anzahl der Einträge in der Datenbank ist) aus und löschst diese.
Dann bist du kannst du dir immer sicher sein, dass das eindeutig ist, und das super effizient.
Das was ich geschrieben habe nach oben, da wo bInfo auch schon vorher war.
Schreibs mal so:
enum bizinteriors{
Name[35],
Interior,
Welt,
Biztype,
Float:IX,Float:IY,Float:IZ// Exit Position
}
static const BizInts[][bizinteriors]={
{"Dummy",0,0,0,0.0,0.0,0.0},
{"24/7",17,1,1,/*InteriorPos*/-25.884498,-185.868988,1003.546873}
};
enum bizinfo{
db_bid,
biztype,
biztype2,
bwaren,
bbesitzer[24],
bbizkasse,
beintritt,
Float:bX,
Float:bY,
Float:bZ,
bInterior,
bopentime,
bmaxwaren,
bizbefehl
};
//weiter unten
new bInfo[sizeof(BizInts)][bizinfo];
Alles anzeigen
Das zwingt dich, dass jedes Biz, jetzt einen Interior benötigt, den du angeben musst, wenn dir das "anfangs" lieber ist..wieso auch immer ![]()
Und wie gesagt, lösche MAX_BIZ. (Vorerst. Wenn du dynamische Interiors verwendest, kannst es ja wieder einführen. Wobei es nicht nötig ist, da du ja sizeof überall verwendest und sich das dadurch dynamisch anpasst, wenn du die größe von dem Array ändert).
Jetzt ist es auch egal, ob du sizeof(bInfo) oder sizeof(BizInts) verwendest, da die den gleichen Wert haben ![]()