Ich finde irgendwie den Fehler nicht
Du packst random Arrays in dein Query.
Wolltest du evtl ein %s nutzen bei den Spawn_Out[0] oder hast du wirklich die Spalte so genannt? ![]()
Dann probier mal sowas: `Spawn_Out[0]`
Ich finde irgendwie den Fehler nicht
Du packst random Arrays in dein Query.
Wolltest du evtl ein %s nutzen bei den Spawn_Out[0] oder hast du wirklich die Spalte so genannt? ![]()
Dann probier mal sowas: `Spawn_Out[0]`
anscheinend sind das doch nicht die schwarzen schafe
In der Log Datei steht doch, wieso es scheitert, hast dir das eigentlich mal angeschaut? ![]()
[08/25/18 14:09:56] [DEBUG] retrieved and pushed value 'UPDATE `user` SET `Level` = '1', `Money` = '115', `AdminLevel` = '0', `DonateRank` = '0', `UpgradePoint` = '0', `Registered` = '1', `Sex` = '1', `Age` = '27', `OriginHerkunft` = '1', `Muted` = '0', `Bank` = '0', `Deaths` = '1' WHERE `Username` = 'Tstuser01gg''
[08/25/18 14:09:56] [ERROR] error #1054 while executing query "UPDATE `user` SET `Gun7` = '0', `Gun8` = '0', `Gun9` = '0', `Gun10` = '0', `Gun11` = '0', `Gun12` = '0', `Ammo1` = '0', `Ammo2` = '0', `Ammo3` = '0', `Ammo4` = '0', `Ammo5` = '0', `Ammo6` = '0', `Ammo7` = '0', `Ammo8` = '0', `Ammo9` = '0', `Ammo10` = '0', `Ammo11` = '0', `Ammo12` = '0' WHERE `Ùsername` = 'Tstuser01gg'": Unknown column 'Ùsername' in 'where clause'
Etwas stimmt mit deinem String, wo du den Spielernamen speicherst.
Und du solltest deine %d überprüfen, da scheint ein Fehler zu sein...
Unterlasse doch bitte vollzitate
Aufruft ? Was ist das xD
Aufruf...
denke mal die letzten beide
..ja das sage ich seid 3 Posts...printe das doch einfach mal und prüfe es dadurch ![]()
Brauchst du nicht, es reicht ja, wenn du noch ein print jeweils zu dem Aufruft packst ![]()
Denn ich glaube, der letzte Codeblock, wo du die letzten beiden Querys drinnen hast, wird einfach nicht aufgerufen.
Kriege nur 6 "wurde abgerufen" nachrichten
Keine Ahnung, was du da machst, wo sind denn die prints dazu?
Und bei dem letzten hast du das query auskommentiert, da kann also nichts kommen und bei dem anderen, wird dieser Codeblock denn überhaupt aufgerufen?!
OnPlayerDeath
Ein Fehler tritt schonmal unter OnPlayerDeath.
Du prüfst nicht, ob killerid != INVALID_PLAYER_ID ist.
Und dadurch kommt es zu Fehlern in einem Array.
Bei deinen Querys sieht doch alles gut aus:
[INFO] Callback successfully executed.
//Edit:
Gibt es eine Möglichkeit bei einem Array der ( Primary ID ) einen Wert hinzuzufügen ohne sowas z.b
Ja generell schon, aber bei genau so einer Art von Array, ist das nicht ohne weiteres möglich.
Aber ist ja auch nicht schlimm, da das ja nur 1x unter OnGameModeInit aufgerufen wird und gut ist ![]()
Ahh ja, wird wohl ein realistic Roleplay Server ![]()
Wie auch immer man wem in den Arsch kacken will.
Aber anyway um dir das zu machen, müssen wir erst ein paar Dinge wissen:
der server ist dann abgestürzt nachdem ich ihn velrassen habe
Dann lade mal das crashdetect Plugin, das sagt dir dann genau, wieso er gecrasht ist ![]()
Hab den string query mal auf 1000 gesetzt
Kann man machen, aber ich denke 512 hätten fürs Erste auch gereicht ![]()
Ich hatte da mal was geschrieben gehabt: Easy Saving
Damit kannst du enums einfach speichern.
Der nimmt direkt die Werte aus dem enum und speichert die.
In dem Thread ist ja alles beschrieben ![]()
Die Frage ist, was genau willst du machen und was genau soll "dynamisch" sein?
Zeig uns mal die ganze Fehlermeldung, also mit Zeilen und dann zeige uns bitte auch die Zeilen ![]()
return eingebaut
Damit zerstört du aber die Funktion der Schleife, deshalb sagte ich break. (Also für andere Spieler geht dann die Schleife evtl nicht mehr.)
public TachoTimer()
{
for(new i=GetPlayerPoolSize(),string[32]; i!=-1; i--)
{
if(!IsPlayerConnected(i) || !IsPlayerInAnyVehicle(i)) continue;
format(string,sizeof string,"Tank: %i L",GetVehicleTank(GetPlayerVehicleID(i)));
PlayerTextDrawSetString(i,Tacho_Tank[i],string);
}
return 1;
}
stock GetVehicleTank(veh)
{
for(new v; v<MAX_VEHCILES; v++)
{
if(playerCar[v][p_car_id_x] == veh) return floatround(playerCar[v][p_car_fuel],floatround_round);
if(fcarInfo[v][fcar_id_x] == veh) return floatround(fcarInfo[v][fcar_fuel],floatround_round);
if(ahCarInfo[v][ah_car_id_x] == veh) return floatround(ahCarInfo[v][ah_car_fuel],floatround_round);
}
return -1;
}
Alles anzeigen
Auslagern hilft auch hier und hält den Code strukturierter.
Hier mal ein Tutorial, was ich geschrieben hatte: Schneller & strukturierter skripten
Du solltest mal sowas wie break benutzen...
Ansonsten rattert der dir da ja eine Schleife mit 500*MAX_CARS*MAX_CARS*MAX_CARS durch...und das halt eben alle paar Millisekunden, je nach timer-intervall.
Das ist ja jetzt nicht so das, was du eigentlich willst..
Es ist an sich nicht sehr schlau den Tank für jedes Fahrzeug in nem anderen Array zu speichern ![]()
Vielleicht sollte man das vereinheitlichen.
Zudem deklariert man keine Variablen in Schleifen...so muss der nämlich immer und immer wieder alles neu allozieren.
Man kann das auch alles kürzen und eleganter schreiben, das werd ich dir machen, wenn du mir sagst wie groß jeweils:
playerCar, fcarInfo und ahCarInfo sind.
Also wie hast du die deklariert?
Keine Ahnung was ich noch tun kann.
...du musst das query unter das format packen...ansonsten ist der query-string doch leer ![]()
+ es gibt extra einen MySQL Log, wo genau drinnen steht, was gerade vor sich geht ![]()
Also wenn du 100 Qualitätsstufen willst, dann so:
enum IllegalInventar_Daten
{
//Illegales Inventar
Float:Cannabis,
Float:CrystalMeth,
Float:Heroin,
Float:Kokain
};
new IllegalInventarRPPlus[MAX_PLAYERS][RPPlus_Daten][100];
//Und geben tust du dann dem Spieler es so:
IllegalInventarRPPlus[playerid][Cannabis][0] = 5;
//Die 0 ist die Qualitätsstufe
Alles anzeigen
Dein Fehler ist die Initialisierung, du kannst ja nicht einfach eine 3 hinschreiben und hoffen, dass der dann das ganze Array so initialisiert.
Das ist etwas komplizierter mit {3, ...} wäre es z.B. möglich.
Habs zwar noch nie benutzt, aber bin ziemlich sicher, dass das auch über localhost geht ![]()
https://github.com/SmItH197/St…steamauth/SteamConfig.php
Da einfach den API-Key rein und dann kannst du es ja ausprobieren ![]()
Gerade erst gemerkt, dass du mit slot 1 beginnst, also musst du natürlich 64+slot rechnen, damit du bei A anfängst ![]()
Und du hast noch einen kleinen Denkfehler gemacht, wenn ich mir deine Querys so ansehe.
SELECT * FROM user WHERE id='%i'
Da suchst du nur nach dem Slot.
Das bedeutet ja aber, dass es nur ein Haus geben kann ![]()
Du solltest dir da evtl nochmal ein paar Tutorials anschauen, wie so Häuser Systeme realisiert werden, denn du brauchst hier halt auch noch eine HausID, damit du von diesem Haus die Mieter abfragen kannst ![]()
sizeof ShowTenands[playerid]
Das hier ist dein Fehler ![]()
Das müsste: sizeof(ShowTenans[]) heißen.
Allerdings würde ich dir gerne auch ein paar Optimierungstipps geben, schau mal hier rein: Schneller & strukturierter skripten
Gibt viele Möglichkeiten den unteren Teil extremst zu kürzen, hier mal eine trickige:
public OnUserLoadTenands(playerid,slot)
{
new name[MAX_PLAYER_NAME];
cache_get_field_content(0,"username",name,dbhandle,sizeof name);
format(ShowTenands[playerid],sizeof ShowTenands[],"%s\nMieter %c: %s",ShowTenands[playerid],64+slot,name);
return 1;
}
%c macht aus einer Zahl, einen Character, nach der ASCII Tabelle: https://www.torsten-horn.de/techdocs/ascii.htm
65 = A
Dementsprechend sind B..C..D
66,67,68, also immer 64+slot ![]()
//Edit: Gerade gesehn, kann ja nur einen Slot geben ![]()
Bei einem aktiven Team-Mitglied läuft der Dialog, ab 2 nicht mehr.
Da ich das gerade geschrieben habe: Schneller & strukturierter skripten
Schau dir das mal an, eventuell hilft dir das ein wenig ![]()
Hast du es denn mal ohne globalen String ausprobiert und tritt das Problem nur bei dir auf oder den anderen auch?
Hallo Brotfische,
in diesem Tutorial will ich mal ein paar Tipps zum Thema Scripting geben.
Immer wieder fallen mir bestimmte Muster auf, die gerade bei etwas unerfahrenen Skriptern passieren.
Hier gehe ich nun mal ein wenig auf Fehlerquellen ein und versuche diese gut darzustellen.
1. Globale Strings
Also sowas sehe ich in letzter Zeit wieder relativ häufig.
//Oben im Skript:
new DialogString[4096];
CMD:1(..)
{
format(DialogString);
}
CMD:2(..)
{
format(DialogString);
}
Alles anzeigen
Das ist zwar eine "nette Idee", allerdings birgt das viele Risiken und ist nicht so dolle, da es zu Überschreibungen und somit zu Fehlern kommen kann.
Also einfach für jeden Dialog eine passende lokale String Variable erstellen.
2. Wiederholungen
Etwas, was man eigentlich als Programmierer unbedingt vermeiden will.
Denn je öfter man etwas neu bzw versucht gleich zu machen, desto höher die Wahrscheinlichkeit, dass Fehler auftreten.
Das ist auch eigentlich das Haupt-Anliegen von meinem Thread. Ich will euch ein wenig dafür sensibilisieren.
Wir schauen heute mal auf Commands, da sich hier wohl mit am Meisten wiederholt.
Viele verwenden ja einen "CMD-Prozessor", das ist schon mal sehr gut.
Nehmen wir mal ocmd.
Viele verstehen die Reihenfolge nicht.
Es gibt einen Callback: BeforePlayerCommandText dieser wird aufgerufen, bevor die ocmd:xxx(..); ausgeführt werden. Wenn man dort return 0; wiedergibt, dann werden diese auch nicht ausgeführt.
Besonders sowas sehe ich sehr oft:
ocmd:bla(playerid)
{
if(!Spieler[playerid][pLoggedIn]) return SCM(playerid,-1,"Du bist nicht eingeloggt");
}
ocmd:bla2(playerid)
{
if(!Spieler[playerid][pLoggedIn]) return SCM(playerid,-1,"Du bist nicht eingeloggt");
}
ocmd:bla3(playerid)
{
if(!Spieler[playerid][pLoggedIn]) return SCM(playerid,-1,"Du bist nicht eingeloggt");
}
Alles anzeigen
Und ich denke mir...wieso?! Wieso macht man sich die Mühe, das immer wieder zu schreiben.
public BeforePlayerCommandText(playerid,cmdtext[])
{
if(!Spieler[playerid][pLoggedIn])
{
SCM(playerid, -1,"Du bist nicht eingeloggt!");
return 0;
}
return 1;
}
So, dann hat man es einmal geschrieben und es gilt für alle Befehle.
Nun, kommen wir zu weiteren Dingen.
Viele glauben, dass es einen Performanz Verlust ergibt, wenn Sie anstatt SendClientMessage nur SCM schreiben, wenn es wie folgt definiert ist:
#define SCM SendClientMessage
Aber nur nochmal für alle, dem ist nicht so!
Das Makro wird durch den Pre-Compiler ersetzt beim kompilieren des Skriptes. Dadurch ergibt sich dadurch 0 Verlust, man spart sich lediglich viel schreiben.
Somit ist es sehr sinnvoll viele Abkürzungen zu haben (am Besten in einem eigenen Include, damit es übersichtlicher ist, nur Makros):
//Hier mal einige Beispiele zur Inspiration:
#define SCM SendClientMessage
#define SCMT SendClientMessageToAll
#define SPD ShowPlayerDialog
#define Freeze(%0) TogglePlayerControllable(%0,0)
#define UnFreeze(%0) TogglePlayerControllable(%0,1)
#define SetPos(%0,%1,%2,%3,%4) SetPlayerPos(%0,%1,%2,%3),SetPlayerFacingAngle(%0,%4)
#define SetWorld(%0,%1,%2) SetPlayerInterior(%0,%1),SetPlayerVirtualWorld(%0,%2)
Wie man sieht, kann man sich dann sehr viel Schreibarbeit schenken!
Schauen wir uns nun weiter
if(GetPlayerState(playerid) != PLAYER_STATE_DRIVER) return SendClientMessage(playerid, -1, "Du bist nicht der Fahrer eines Wagens!");if(!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, -1, "Ein Spieler mit dieser ID ist nicht online!");
Also diese Statements kennt jeder und in fast jedem Befehl kommt so ein Statement bzw so eine Abfrage vor, die sich wiederholt.
Viele denken jetzt einfach, jo dann schreib ich das immer wieder und wieder hin.
Hier entstehen aber oftmals Rechtschreibfehler oder andere Fehler + es ist sehr Zeitaufwändig + eine Wiederholung.
Also auch hier ist es wieder sinnvoll, am Besten auch wieder in einer Include oder oben im Skript auslagern, vordefinierte Nachrichten zu machen.
Gerade bei so Fehlermeldungen ist dies sehr einfach.
In 100% der Fälle, werden diese nämlich immer an playerid geschickt, da das der Spieler ist, der den Befehl eingegeben hat.
Mit diesem Wissen, können wir nun folgendes machen:
//Mal wieder zur Inspiration ein paar:
#define NO_ADMIN SCM(playerid,rot,"Sie besitzen nicht die notwendigen Rechte, für diesen Befehl!")
#define NO_RANGE SCM(playerid,rot,"Dieser Spieler befindet sich nicht in Ihrer Nähe!")
#define NO_LEADER SCM(playerid,rot,"Sie sind kein Leader einer Fraktion!")
#define NO_CON SCM(playerid,rot,"Ein Spieler mit dieser ID oder mit diesem Namen ist nicht online!")
#define NO_YOU SCM(playerid,rot,"Diesen Befehl kannst du nicht auf dich selbst anwenden!")
#define NO_VEH SCM(playerid,rot,"Sie befinden sich in keinem Auto!")
#define NO_FRAK SCM(playerid,rot,"Sie befinden sich in keiner Fraktion!")
#define NO_COP SCM(playerid,rot,"Sie befinden sich in keiner Staatsfraktion!")
Und dann kann man einfach folgendes schreiben:
if(!IsPlayerConnected(giveplayerid)) return NO_CON;
NO_CON ist in dem Fall einfach nur die Abkürzung für NO_CONNECTION, also keine Verbindung.
Kann man natürlich alles wählen wie man will.
Viele werden jetzt sagen, jo, das kann man doch noch weiter vereinfachen, z.B.:
#define NO_CON(%0) if(!IsPlayerConnected(%0)) return SCM(playerid,rot,"Ein Spieler mit dieser ID oder mit diesem Namen ist nicht online!")
Verwendung wäre dann:
NO_CON(giveplayerid);
Ja, das wäre auch möglich, aber es würde die Code-Logik ein wenig zerstören, da man jetzt nicht mehr nachvollziehen kann was genau dieses Makro macht.
Man sieht keine if-Abfrage mehr usw.
Wenn man alleine an einem Skript arbeitet kann man das machen, aber sobald mehrere involviert werden, macht es das nur noch komplizierter.
Standardisierte Nachrichten hingegen, machen es noch einfacher, dass man nicht plötzlich von dem einen Command diesen und vom anderen diesen Wortlaut bekommt ![]()
Noch ein Punkt sind wiederholende Funktionen.
Mal ein Beispiel, wir wollen rausbekommen, ob ein anderer Spieler in unserer Nähe ist.
Ohne Mist, ich habe das in so vielen Skripts gesehen, dass ich das dann alles immer 1:1 von Befehl zu Befehl wiederholt.
new Float:x,Float:y,Float:z;
GetPlayerPos(playerid,x,y,z);
if(!IsPlayerInRangeOfPoint(pID,5.0,x,y,z)) return NO_RANGE;
Das sind zwar nur 2 Zeilen zusätzlich, aber warum?
Sofort eine Funktion daraus machen und auslagern. Viele meinen jetzt sicherlich, joa ist doch eigentlich unnötig, die 2 Zeilen da.
stock InRange(playerid,pID,Float:r)
{
new Float:x,Float:y,Float:z;
GetPlayerPos(playerid,x,y,z);
return IsPlayerInRangeOfPoint(pID,r,x,y,z);
}
//Abfrage lautet dann eben nur:
if(!InRange(playerid,pID,5.0)) return NO_RANGE;
PS: Viele machen auch die geschweifte Klammer immer direkt hinter den Parametern, also stock InRange(playerid,pID,Float:r) { Das kann man machen, wenn man es übersichtlicher findet. Aber nur zum Verständnis, dies macht keinen Unterschied. Es spart weder Ressourcen, noch sonst etwas.
Es ist wichtig zu verstehen, dass es bei gutem Scripting bzw generell programmieren um die Übersichtlichkeit geht.
Okay, also wieso ist es jetzt gut, dass wir das ausgelagert haben?
Also zum einen ist klar, dass wenn wir das 50x brauchen, wir uns 100 Zeilen Schreibarbeit sparen ![]()
Zum anderen ist es so, dass wenn uns jetzt auffällt, dass wir auch noch den Zustand überprüfen müssen von dem Spieler, bspw soll das natürlich nicht klappen, wenn der eine im Spectate Modus ist oder gerade spawnt.
Also deklarieren wir uns wieder eine Funktion:
stock IsValidState(p)
{
switch(GetPlayerState(p))
{
case PLAYER_STATE_WASTED,PLAYER_STATE_SPAWNED,PLAYER_STATE_SPECTATING: return 0;
}
return 1;
}
Und können das jetzt ganz einfach in unserer Funktion ergänzen und somit wird es für alle ergänzt.
stock InRange(playerid,pID,Float:r)
{
new Float:x,Float:y,Float:z;
GetPlayerPos(playerid,x,y,z);
return (IsValidState(playerid))?IsPlayerInRangeOfPoint(pID,r,x,y,z):0;
}
Denn es wäre sehr mühsam das überall zu ergänzen und wenn man es ausgelagert hat, ist es sehr einfach solche "Bugs" zu beheben ![]()
Selbiges gilt auch zum Beispiel für Schleifen für Admin-Nachrichten.
Sehe da immer wieder Leute die da sowas machen:
for(new i; i<MAX_PLAYERS; i++)
{
if(IsPlayerConnected(i) && IsPlayerAdmin(i))
{
SendClientMessage(i,-1,string);
}
}
Das wird dann immer und immer wieder kopiert oder selbst geschrieben unter alles mögliche gepackt.
Funktionen Leute, Funktionen.
stock SendAdminMessage(color, const msg[])
{
for(new i=GetPlayerPoolSize(); i!=-1; i--)
{
if(IsPlayerConnected(i) && IsPlayerAdmin(i)) SCM(i,color,msg);
}
return 1;
}
Dann kann man wieder hier ergänzen, wenn man Admin-Abfragen ändert o.ä. anstatt dann wieder durchs ganze Skript zu rennen.
Okay, hoffen wir mal, die Punkte sind jetzt klar und ich habe euch ein wenig dafür sensibilisiert.
3. switch oder if?
Da ich das gefühlt sehr sehr oft erwähnen muss und irgendwie viele auch nicht wissen hier nochmal eine kleine Erläuterung:
Kurz und knackig, sobald ihr überlegt switch oder if, dann immer switch wählen.
if-Abfragen haben den Nachteil, dass diese erst alle einzeln geprüft werden müssen. Wohingegen bei einem switch direkt dahin gesprungen wird.
Allerdings gibt es auch manchmal Bereiche, wo man weder if, noch switch verwenden sollte.
Schauen wir uns dazu mal ein Beispiel an.
Nehmen wir mal als Beispiel Admin-Rang-Namen.
/*
Vorab mal kurz etwas zur String-Zuweisung:
Viele nutzen dafür ja format, das ist auch richtig, aber nur dann, wenn wir nicht wissen, welche Größe der String den wir zuweisen hat.
Ansonsten können wir auch einfach eine direkte Zuweisung machen, die auch 1000x schneller ist :)
Direkte String-Zuweisung ist immer schneller als alles andere.
Mal einige Funktionen der Geschwindigkeit her sortiert:
Direkte Zuweisung - super schnell
stract - normal
strmid - sehr langsam
format - super langsam
*/
//if-Variante
//->schlechteste
stock GetAdminRangName(playerid)
{
new string[64];
if(Spieler[playerid][AdminRang] == 0) string = "User"; //Direkte Zuweisung
else if(Spieler[playerid][AdminRang] == 1) string = "Moderator";
//usw
return string;
}
Alles anzeigen
So, das ist viel Schreibarbeit und dann auch noch langsam ![]()
Das sollte nicht unser Ziel sein.
Nehmen wir switch:
stock GetAdminRangName(playerid)
{
new string[64];
switch(Spieler[playerid][AdminRang])
{
case 0: string = "User"; //Direkte Zuweisung
case 1: string = "Moderator";
//usw
}
return string;
}
Alles anzeigen
Das ist schon um einiges besser und auch viel weniger Schreibarbeit.
Aber, da diese Dinge, ja immer konstant bleiben, können wir sie auslagern:
stock const admin_rang[][] = {"User","Moderator",...};
#define GetAdminRangName(%0) admin_rang[Spieler[%0][AdminRang]]
Dann brauchen wir auch nicht mal mehr eine Funktion, sondern können das direkt so nutzen.
Natürlich kann und sollte man nicht alles in den Heap Speicher auslagern (globale Variablen landen im Heap, lokale Variablen landen im Stack).
Allerdings kann man so ein array ja auch kurzfristig im Stack erstellen und dann abfragen.
So schenkt man sich meistens sehr viel Schreibarbeit ![]()
4. Variablen wiederverwenden
Was ich weiterhin oft sehe ist sowas hier:
Und dann verwendet man auf den kleinen format und packt mit strcat das alles in den mittelString.
Und dann den mittelString in dem megaString und das halt ein paar mal, bis alles im megaString ist.
Okay...kann man machen, allerdings reicht es hier nur den megaString zu haben, der meistens auch viel zu groß ist.
Also gehen wir als Erstes auf die Größe ein.
Man kann meistens abzählen, wie groß so ein String ist, na klar, keiner macht sich die Mühe, das auf das Zeichen genau zu zählen. Das ist auch nicht nötig es reichen gute Schätzungen.
Hier mal ein Beispiel:
Das ist natürlich viel zu groß.
Es reicht hier einfach folgendes zu machen:
Es ist nicht unperformant sowas zu tun, da solche Konstanten Additionen vom pre-compiler ausgerechnet werden und somit hat man eben am Ende dort einen festen Wert stehen.Also ist es sehr gut sowas zu verwenden, da dann weniger Speicher reserviert wird.
Aber jetzt zur Wiederverwendung:
new string[512];
format(string,sizeof(string),"Hallo du bist");
format(string,sizeof(string),"%s ein Idiot!",string);
So format leert auch gleichzeitig immer den String, deshalb ist es u.a. so langsam. Wir können also mit dem %s Trick den vorherigen String wieder dran hängen und können so Ketten bilden.
Dann brauchen wir weder andere strings, noch strcat.
Man kann übrigens auch direkte Zuweisungen bei der Deklaration machen:
Und richtig, da wir hier keine Variable formatieren im format, sollten wir hier lieber strcat nutzen.
So mal ein bisschen zum Verständnis.
Schlusswort
Also erstmal, vielen Dank, dass du mein Tutorial bis hier hin gelesen hast ![]()
Man kann zusammenfassend sagen, dass man es vermeiden sollte, viele Dinge zu wiederholen und mit Tricks vieles einfacher schreiben kann.
Ich werde das hier mit der Zeit wahrscheinlich noch ein wenig anpassen und ergänzen.
Also wenn ihr weitere solcher Fehler kennt, schreibst einfach mal in den Thread, dann können wir ein wenig darüber reden ![]()