wie währe es einfach das rcon pw im script zu setzen ?
und denn ohne rcon keine adminrechte
wenn dann einer die amx klaut verliert der ohne adminrechte schnell das interesse
Da strapaziert man Nerven, sichert jedoch das Script nicht.
wie währe es einfach das rcon pw im script zu setzen ?
und denn ohne rcon keine adminrechte
wenn dann einer die amx klaut verliert der ohne adminrechte schnell das interesse
Da strapaziert man Nerven, sichert jedoch das Script nicht.
Man müsste nicht einmal den Umweg über PHP gehen:
Man könnte auch mit Sockets arbeiten, dadurch kann das PHP-Script direkt Antworten auf jegliche Anfragen senden.
Was sind die <header> Tags?, kenn ich garnicht.
Versuchs mal ohne <header></header>
Ist erst mit HTML 5 gekommen und ist in den meisten älteren Browsern garnicht nutzbar.
Ebenfalls:
<style>-Tags kommen in <head>, in Body funktioniert nur Inline-CSS.
Wieso denn GetVehicleModel()? im Array Trucker[] stehen doch die VehicleIDs (= Rückgabewerte von AddStaticVehicleEx()). Demnach solltest du GetPlayerVehicleID() verwenden.
HTML-Color-Code: #FF6142 (Rot: 255, Grün: 97, Blau: 66).
Nach Color-Picker: 0xFF6142FF
Am sinnvollsten wäre es per CSS den body zu bearbeiten (background-image, background-repeat, etc.).
Warum baust du die Schleifen ineinander, wenn es zwei Paar Schuhe sind?
"Reason: Dead Connection" - Na dann überprüfe doch mal deine Datenbank und deren Daten im Script.
Kann mir einer nen link schicken von dem neusten mysql plugin und inc.?
Wenn du die alten Funktionen verwendest, dann benötigst du auch den alten Plugin. Mit den neusten Versionen wirst du nicht sehr viel weiter kommen.
Innerhalb weniger Millisekunden derartig viele 'Incoming Connections ...' - Sind dies NPCs?
Nicht schlecht, doch das Geschriebene in Grün Irritiert sehr ... blicke da nicht durch.
Du hast es gut BEschrieben und alles doch ein bissel zuviel
Mit dem 2. Teil des Tutorials ist nun auch ein fertiger Quellcode hinzugekommen ohne Kommentare. Demnach könnte dies evt. etwas für dich sein, um sich das System als Ganzes anzuschauen.
'rft_mysql' erscheint mir in diesem Fall ein Include zu sein. Besorge dir alle Includes und Plugins vom alten Scripter. Trotzdem solltest du 'Fragen' nicht gerade im Showroom stellen.
Wichtigste Grundbefehle
Um das Haussystem nun belebbar zu machen werde ich im folgenden die wichtigsten Befehle für Admin und Spieler hinzufügen. Dabei sei zu sagen, dass diese sehr ausbaufähig sind und dieses Script weiterhin nur als Grundbaustein für ein ausgereiftes System dient. Beginnen wir damit, dass Spieler die Häuser betreten und verlassen können. Dabei kommen wir nun auf die InteriorID zu sprechen, welche eigentlich keine InteriorID ist, sondern einen Index im Array der vordefinierten Interiors darstellt. Sehet selbst:
// Neuer Enumerator, welcher Informationen über vorhandene Interiors halten soll.
enum hausInteriorDataEnumerator {
Float:iX,
Float:iY,
Float:iZ,
iInterior
};
new hausInteriorData[][hausInteriorDataEnumerator] = { // Neuer Array in Verbindung mit Enumerator mit direktem definieren der Interiors.
{2495.8079, -1694.1421, 1014.7422, 3}, // iX, iY, iZ, iInterior
{267.3732, 304.9254, 999.1484, 2},
{1.5435, -3.2401, 999.4284, 2},
{1262.4308, -785.4622, 1091.9063, 5}
};
COMMAND:enter(playerid, params[]) { // /enter zum Betreten der Häuser.
for(new i = 0; i != MAX_HOUSES; i++) { // Durchlaufen aller Häuser, um zu testen, ob der Spieler einer aller Häuser betreten kann.
if(IsPlayerInDynamicCP(playerid, hausInfo[i][hCpID])) { // Wenn der Spieler im Checkpoint eines Hauses steht, dann ...
// ... setze die SpielerPosition in den Interior des Hauses (InteriorArray[Index][Koordinate]).
SetPlayerPos(playerid, hausInteriorData[hausInfo[i][hInterior]][iX], hausInteriorData[hausInfo[i][hInterior]][iY], hausInteriorData[hausInfo[i][hInterior]][iZ]);
SetPlayerVirtualWorld(playerid, hausInfo[i][hID]); // ... setze die virtuelle Welt eindeutig, damit kein Haus ein anderes stören kann.
SetPlayerInterior(playerid, hausInteriorData[hausInfo[i][hInterior]][iInterior]); // ... setze den Interior mithilfe der vordefinierten Werte.
break;
}
}
return 1;
}
COMMAND:exit(playerid, params[]) {
new playerWorld = GetPlayerVirtualWorld(playerid); // Auslesen der oben gesetzen virtuellen Welt.
if(playerWorld != 0) { // Wenn der Spieler nicht im Freien ist (AUTO_INCREMENT wird niemals 0 als Index nehmen!), dann ...
for(new i = 0; i != MAX_HOUSES; i++) { // ... durchlaufe alle möglichen Häuserdaten.
if(hausInfo[i][hID] == playerWorld) { // Wenn die eindeutige Welt der Welt der Welt des Hauses entspricht, dann ...
// ... teste, ob der Spieler in der Nähe der Türe im Interior ist.
if(IsPlayerInRangeOfPoint(playerid, 3, hausInteriorData[hausInfo[i][hInterior]][iX], hausInteriorData[hausInfo[i][hInterior]][iY], hausInteriorData[hausInfo[i][hInterior]][iZ])) {
SetPlayerPos(playerid, hausInfo[i][hX], hausInfo[i][hY], hausInfo[i][hZ]); // Falls ja, dann setze den Spieler zum Eingang des Hauses, ...
SetPlayerVirtualWorld(playerid, 0); // ... setze die virtuelle Welt auf 0 (= Außen) und ...
SetPlayerInterior(playerid, 0); // ... setze den Interior ebenfalls auf 0.
}
break;
}
}
}
return 1;
}
Da das System dynamisch sein soll, wollen wir also nun im folgenden per Befehl im Spiel Häuser erstellen können. Dazu ist wieder wie zuvor ein Query benötigt, jedoch diesmal ein Callback nur, um per mysql_insert_id() den Index, der per AUTO_INCREMENT gesetzt wurde, auszulesen und abzuspeichern. Wir erstellen also einen neuen Befehl und einen Callback, der zuerst geforwarded werden muss:
forward OnPlayerCreateHouse(hausID); // Forwarded des Publics wie zuvor.
COMMAND:createhouse(playerid, params[]) {
if(IsPlayerAdmin(playerid)) { // Nur Admins sollten in der Lage sein, Häuser zu erstellen.
new interior, preis; // Vordefinieren der benötigten Variablen.
// Aufsplitten der eigegebenen Parameter in einzelne Variablen mithilfe des Custom-Delimiter. Falls falsche Werte, wird eine Nachricht ausgegeben.
if(sscanf(params, "ii", interior, preis)) return SendClientMessage(playerid, COLOR_GREY, "Befehl: /createhouse [Interior] [Preis]");
// Der Interior muss zuvor im hausInteriorData-Array definiert sein.
if(interior >= sizeof(hausInteriorData)) return SendClientMessage(playerid, COLOR_GREY, "Dieser Interior steht nicht zur Verfügung.");
// Es wäre unrealistisch, den Preis unter 0 zu setzen.
if(preis < 0) return SendClientMessage(playerid, COLOR_GREY, "Sie müssen einen Preis für das Haus wählen, welcher über null liegt.");
for(new i = 0; i != MAX_HOUSES; i++) { // Durchlaufen aller Häuserdaten, um einen freien Slot zu finden.
if(hausInfo[i][hID] == 0) { // Sobald einer der Slots frei ist, dann ...
new Float:X, Float:Y, Float:Z, query[160];
GetPlayerPos(playerid, X, Y, Z); // ... jetzigen Standpunkt des Admins herauslesen.
hausInfo[i][hPreis] = preis; // Setzen der neuen Hausdaten (Preis, Interior, Koordinaten).
hausInfo[i][hInterior] = interior;
hausInfo[i][hX] = X;
hausInfo[i][hY] = Y;
hausInfo[i][hZ] = Z;
CreateHouseOnMap(i); // Praktische Realisierung des Hauses auf der Karte.
// Abspeichern der Daten in der Datenbank - Formatierung per mysql_format().
mysql_format(sqlHandle, query, "INSERT INTO `breadfish_houses` (`hPreis`, `hInterior`, `hX`, `hY`, `hZ`) VALUES ('%i', '%i', '%f', '%f', '%f')", hausInfo[i][hPreis],
hausInfo[i][hInterior],
hausInfo[i][hX],
hausInfo[i][hY],
hausInfo[i][hZ]);
// Absenden des Querys mit Aufruf des Callbacks 'OnPlayerCreateHouse' nach Vollendigung des Querys.
mysql_function_query(sqlHandle, query, false, "OnPlayerCreateHouse", "i", i);
return 1; // Stoppen des Befehls, sobald das Haus erstellt wurde.
}
}
// Es konnte kein Haus erstellt werden, da alle Slots belegt sind - Fehlermeldung in Console!
print("Haussystem: Alle Häuserslots sind belegt, heben Sie MAX_HOUSES an, um neue Häuser erstellen zu können.");
} else {
SendClientMessage(playerid, COLOR_GREY, "Sie sind nicht dazu befugt, diesen Befehl zu verwenden.");
}
return 1;
}
public OnPlayerCreateHouse(hausID) {
hausInfo[hausID][hID] = mysql_insert_id(); // Auslesen des Indexes aus der Datenbank, der per AUTO_INCREMENT gesetzt wurde.
return 1;
}
Nachdem nun ein Haus betreten und verlassen werden kann und Häuser von Admins im Spiel erstellt werden können fehlt eigentlich für's erste nur noch die Möglichkeit, Häuser zu kaufen bzw. zu verkaufen. Gehen wir dazu wie folgt vor:
#define COLOR_GREY (0xB5B5B5FF) // Definieren zweier neuer Farben, ...
#define COLOR_GREEN (0x80A05CFF) // zur Verschönerung von Ausgaben.
COMMAND:buyhouse(playerid, params[]) { // /buyhouse, um ein Haus zu kaufen.
for(new i = 0; i != MAX_HOUSES; i++) { // Durchlaufen aller Hausdaten, um ...
if(IsPlayerInDynamicCP(playerid, hausInfo[i][hCpID])) { // ... zu testen, ob der Spieler sich in einem Checkpoint eines Hauses befindet.
// Ein zu kaufendes Haus darf über keinen Besitzer verfügen.
if(!isnull(hausInfo[i][hBesitzer])) return SendClientMessage(playerid, COLOR_GREY, "Dieses Haus ist bereits im Besitz einer anderen Person.");
// Falls der Spieler nicht das entsprechende Geld bei sich hat, soll der Befehl unterbrochen werden.
if(GetPlayerMoney(playerid) < hausInfo[i][hPreis]) return SendClientMessage(playerid, COLOR_GREY, "Sie haben nicht genügend Geld bei sich, um dieses Haus zu finanzieren.");
new labelText[70], query[90], clientName[MAX_PLAYER_NAME];
GetPlayerName(playerid, clientName, sizeof(clientName)); // Auslesen des Spielernamens zum Setzen des neuen Besitzernamens.
GivePlayerMoney(playerid, -hausInfo[i][hPreis]); // Preis zur Finanzierung des Hauses dem Spieler abziehen.
format(hausInfo[i][hBesitzer], MAX_PLAYER_NAME, "%s", clientName); // Neuen Besitzer setzen.
format(labelText, sizeof(labelText), "- Dieses Haus ist in Besitz! -\nBesitzer: %s", hausInfo[i][hBesitzer]); // 3D-Text-Label Text erneuern und ...
UpdateDynamic3DTextLabelText(hausInfo[i][h3DText], HAUS_TEXT_COLOR, labelText); // ... neu setzen.
// Übernehmen des neuen Besitzers in der MySQL Tabelle (Hier ist weder Callback noch Parameter noch Format benötigt, daher 3x "").
mysql_format(sqlHandle, query, "UPDATE `breadfish_houses` SET `hBesitzer` = '%e' WHERE `hID` = '%i'", hausInfo[i][hBesitzer], hausInfo[i][hID]);
mysql_function_query(sqlHandle, query, false, "", "", "");
// Ausgabe an den Spieler, dass er nun im Besitz eines neuen Hauses ist.
SendClientMessage(playerid, COLOR_GREEN, "Herzlichen Glückwunsch {FFFFFF}zu Ihrem neuen Haus.");
}
}
return 1;
}
COMMAND:sellhouse(playerid, params[]) { // /sellhaus, um ein Haus zu verkaufen.
for(new i = 0; i != MAX_HOUSES; i++) { // Durchlaufen aller Hausdaten, um ...
if(IsPlayerInDynamicCP(playerid, hausInfo[i][hCpID])) { // ... zu testen, ob der Spieler sich in einem Checkpoint eines Hauses befindet.
new clientName[MAX_PLAYER_NAME];
GetPlayerName(playerid, clientName, sizeof(clientName)); // Auslesen des Spielernamens zum Vergleich mit den Hausdaten.
// Falls der Spieler der Besitzer ist (isnull(), da strcmp() 0 zurückgibt, wenn eine der Strings NULL ist), dann ...
if(!isnull(hausInfo[i][hBesitzer]) && strcmp(hausInfo[i][hBesitzer], clientName) == 0) {
new labelText[70], query[70];
GivePlayerMoney(playerid, hausInfo[i][hPreis]); // ... Geld zurücküberweisen.
format(hausInfo[i][hBesitzer], MAX_PLAYER_NAME, "%s", EOS); // ... Hausbesitzer in Hausdaten auf NULL setzen.
format(labelText, sizeof(labelText), "- Dieses Haus ist zu kaufen! -\nPreis: $%i", hausInfo[i][hPreis]); // ... 3D-Text-Label Text erneuern und ...
UpdateDynamic3DTextLabelText(hausInfo[i][h3DText], HAUS_TEXT_COLOR, labelText); // ... neu setzen.
// Übernehmen der neuen Hausdaten in der MySQL Tabelle (Hier ist weder Callback noch Parameter noch Format benötigt, daher 3x "").
mysql_format(sqlHandle, query, "UPDATE `breadfish_houses` SET `hBesitzer` = NULL WHERE `hID` = '%i'", hausInfo[i][hID]);
mysql_function_query(sqlHandle, query, false, "", "", "");
// Ausgabe an den Spieler, dass ihm das Geld zurück erstattet wurde.
SendClientMessage(playerid, COLOR_GREEN, "Sie haben Ihr Haus erfolgreich verkauft und den Wert des Hauses überwiesen bekommen.");
}
break;
}
}
return 1;
}
Abschließendes
Sooo ... das meisten hätten wir. Häuser werden nun also wunderbar beim Start des Gamemodes geladen und Spieler sind in der Lage, Häuser zu erstellen, diese zu betreten bzw. zu verlassen und die Häuser zu kaufen bzw. zu verkaufen. Fügen wir noch den letzen Feinschliff hinzu, indem wir noch Ausgaben beim Betreten eines Häusercheckpoints hinzufügen. Dazu verwenden wir einen von Incognito's bereitgestellten Callback (OnPlayerEnterDynamicCP()), welcher einmal beim Betreten eines dynamischen Checkpoints aufgerufen wird. Ebenfalls fügen wir noch die Beendigung der MySQL-Verbindung hinzu, sobald der GameMode gestoppt wird.
public OnGameModeExit() { // Sobald der GameMode geschlossen wird, ...
mysql_close(); // ... wird die Verbindung zur Datenbank gekappt.
return 1;
}
public OnPlayerEnterDynamicCP(playerid, checkpointid) { // Sobald ein Spieler einen gestreamten Checkpoint betritt, ...
for(new i = 0; i != MAX_HOUSES; i++) { // ... durchlaufen wir alle Hausdaten, um zu testen, ...
if(checkpointid == hausInfo[i][hCpID]) { ... ob die CheckpointID der CheckpointID des Hauses entspricht.
if(isnull(hausInfo[i][hBesitzer])) { // Falls dieses Haus nicht im Besitz eines Spielers ist, ...
// ... geben wir folgende Nachricht aus:
SendClientMessage(playerid, COLOR_GREEN, "Dieses Haus steht zum Verkauf offen! Verwende {FFFFFF}/buyhouse{80a05c}, um das Haus zu erwerben.");
} else { // Ansonsten, ...
new message[140];
// ... formatieren wir einen anderen String und geben ihn anschließend dem Spieler aus.
format(message, sizeof(message), "Willkommen an {FFFFFF}%s's{80a05c} Vordach, verwenden Sie {FFFFFF}/enter{80a05c}, um das Haus zu betreten.", hausInfo[i][hBesitzer]);
SendClientMessage(playerid, COLOR_GREEN, message);
}
break;
}
}
return 1;
}
Fertiger Quellcode (ohne Kommentare): Click here!
Als Résumé kann man also stehen lassen, dass ein Haussystem eigentlich recht simpel ist und eigentlich auf sehr wenig Quellcode basiert. Nur das Ausbauen des Scripts durch Befehle und anderen Schnick-Schnack lässt das ganz Haussystem sehr komplex erscheinen, wobei eigentlich immer nur wenige Daten bearbeitet werden müssen. Wichtig ist eben, dass man an ein solches System immer geplant und strukturiert herangeht, da falsche Strukturen einem meist das Einfache im System rauben. Also nehmt euch Zeit, plant, was ihr dazu verwenden könnt und wollt und wie ihr mit den einzelnen Bausteinen umgeht. Ich wiederhole zum Ende des Tutorials nochmals, dass es sich hierbei lediglich um ein Grundgerüst handelt - Untersysteme wie Mietsysteme etc. sind hier nun sehr einfach einzubauen und haben kaum Einfluss auf den Rest des Quellcodes.
So, heute machen wir uns an ein dynamisches Haussystem mit den folgenden Eigenschaften:
Unser Scriptkopf sieht demnach also nun wie folgt aus:
#include <a_samp> // SA:MP Include
#include <a_mysql> // MySQL R7
#include <streamer> // Streamer
#include <sscanf2> // sscanf
#include <zcmd> // ZCMD
main() {}
Links zu den benötigten Plugins & Includes:
Inhalt des Tutorials:
Planung & Umsetzung der MySQL Datenbank
Zu Beginn des Tutorials möchte ich anmerken, dass dieses Tutorial lediglich den Grundbaustein für ein ausgereiftes Haussystem legt. Es soll dazu dienen, eine Art der Realisierung eines Haussystems mit den genannten Hilfsmitteln darzulegen. Dies ist daher weder eine perfekte noch eine ausgereifte Version, sondern ein Grundscript, welches in ca. 1h Arbeit entstand. Anzumerken ist ebenfalls, dass jedes größere System etwas an Planung benötigt. Die grundlegende Planung unseres Systems haben wir durch das Festlegen der Plugins & Includes bereits getan. Gehen wir also genauer auf die Planung der MySQL Datenbank ein - es sollte folgende Eigenschaft erfüllt sein:
Es muss also der Index auf 'PRIMARY' gesetzt werden und 'AUTO_INCREMENT' aktiviert werden - Wir verwenden eine ganze Zahl, also einen Integer (INT) (Bild: http://www.abload.de/img/database13qw5.jpg).
Um unsere Daten in der Datenbank zu speichern, müssen wir uns zuerst überlegen, welche Spalten wir in der Tabelle, in welcher wir unsere Häuser speicher werden, benötigen. Wir arbeiten also an unserem Script weiter, indem wir einen Enumerator erstellen und uns überlegen, was zu speichern sei:
enum hausEnumerator {
hID, // Eindeutige ID des Hauses (Typ: INT)
hPreis, // Preis zum Erwerb des Hauses (Typ: INT)
hBesitzer[MAX_PLAYER_NAME], // Name des Besitzers des Hauses (Typ: VARCHAR ; Größe: MAX_PLAYER_NAME = 24)
hInterior, // Interior des Hauses (nicht die InteriorID - mehr dazu später!) (Typ: INT)
Float:hX, // X-Koordinate des Hauseingangs (Typ: FLOAT)
Float:hY, // Y-Koordinate des Hauseingangs (Typ: FLOAT)
Float:hZ // Z-Koordinate des Hauseingangs (Typ: FLOAT)
};
Die Tabelle sollte also nach folgendem Schema erstellt werden (Dies ist ein Beispiel!):
CREATE TABLE IF NOT EXISTS `breadfish_houses` (
`hID` int(11) NOT NULL AUTO_INCREMENT,
`hPreis` int(11) NOT NULL,
`hBesitzer` varchar(24) NOT NULL,
`hInterior` int(11) NOT NULL,
`hX` float NOT NULL,
`hY` float NOT NULL,
`hZ` float NOT NULL,
PRIMARY KEY (`hID`)
);
Auslesen der MySQL Datenbank
Also, kommen wir als nächstes ans eigentliche Scripting, um was es in diesem Tutorial ja eigentlich gehen soll. Fangen wir damit an, uns eine Verbindung zu MySQL aufzubauen, damit wir auf die Werte per Queries zugreifen können:
#define SQL_DATABASE "database" // Datenbankname
#define SQL_HOST "localhost" // Hostname
#define SQL_USER "root" // Username
#define SQL_PASSWORD "" // Passwort
new sqlHandle; // Variable zum Zwischenspeichern des MySQL-Handles (Teilweise für Funktionen als Parameter benötigt!).
public OnGameModeInit() { // Wir bauen eine Verbindung auf, sobald der Gamemode geladen wird.
// Verbindung mit den oben definierten Parametern aufbauen und Handle übergeben.
sqlHandle = mysql_connect(SQL_HOST, SQL_USER, SQL_DATABASE, SQL_PASSWORD);
// Testen, ob eine Verbindung besteht - Falls nein, Fehlermeldung + Exit!
if(mysql_ping(sqlHandle) != 1) {
print("MySQL Error: Es konnte keine Verbindung zur Datenbank hergestellt werden.");
SendRconCommand("exit");
}
return 1;
}
Eine Verbindung zur Datenbank sollte nun also stehen - ihr könnte ja bereits den Gamemode einmal starten, um zu testen, ob die Console sich wieder schließt und eine Fehlermeldung ausgibt, oder ob die Verbindung besteht. Gehen wir weiter zum Laden den Häuser. Dies werden wir ebenfalls im OnGameModeInit() Callback durchführen, sodass alle Häuser geladen sind, sobald der erste Spieler den Server betritt. Im folgenden werden cache-Funktionen, die in BlueG's MySQL R7 zum ersten mal zur Verfügung stehen, verwendet. Bei Problemen könnt ihr euch auch Tutorials über das Caching anschauen (die gibt es auf english als auch auf deutsch). Wir erweitern den Callback also um eine Funktion, welche unseren ersten Query ausführen soll:
public OnGameModeInit() { // Wir bauen eine Verbindung auf, sobald der Gamemode geladen wird.
// Verbindung mit den oben definierten Parametern aufbauen und Handle übergeben.
sqlHandle = mysql_connect(SQL_HOST, SQL_USER, SQL_DATABASE, SQL_PASSWORD);
// Testen, ob eine Verbindung besteht - Falls nein, Fehlermeldung + Exit!
if(mysql_ping(sqlHandle) != 1) {
print("MySQL Error: Es konnte keine Verbindung zur Datenbank hergestellt werden.");
SendRconCommand("exit");
}
/* Query ausführen, welcher unsere Werte aus der Tabelle holt.
Parameter: connectionHandle, query[], bool:cache, callback[], format[], {Float, ... }
Caching aktiviert, da wir Werte auslesen (SELECT) und später übergeben müssen. All dies werden wir in OnGameModeLoadHouses tun.
*/
mysql_function_query(sqlHandle, "SELECT `hID`, `hPreis`, `hBesitzer`, `hInterior`, `hX`, `hY`, `hZ` FROM `breadfish_houses`", true, "OnGameModeLoadHouses", "", "");
return 1;
}
Zu beachten beim Formulieren von Queries ist:
Da MySQL R7 alle Queries, die ausgeführt werden, threaded (das heißt auf einem anderen Thread weiterlaufen lässt, um den Programmfluss nicht zu stoppen, bis der Query ein Ergebnis liefert) müssen wir nun den Umweg über diesen Callback gehen, der auf dem 2. Thread laufen wird (Extra Tutorial mit MySQL R6 zum gleichen Sachverhalt: Click here!). Der Grund liegt darin, dass MySQL R7 die Queries in eine Art Warteschlange einreiht und Schritt für Schritt abarbeitet. Dabei läuft der eigentliche Quellcode weiter und wartet nicht auf eine Antwort. Da es so ohne Warten auf Beendigung des Queries dazu kommen kann, dass wir NULL-Werte einlesen, lassen wir den Callback nach Erreichen eines Ergebnisses aufrufen. Daher gehen wir nun wie folgt vor:
#define MAX_HOUSES (200) // Definieren der Häuserslots - Kann jederzeit erhöht werden, jedoch desto mehr, desto mehr Arbeit für das Script.
new sqlHandle,
hausInfo[MAX_HOUSES][hausEnumerator]; // Array, welcher in Verbindung mit dem Enumerator die Daten der Häuser hält.
forward OnGameModeLoadHouses(); // Wir verfügen über einen public-Callback - dieser muss geforwarded werden -vor- Gebrauch der Funktion.
public OnGameModeLoadHouses() {
new rows, fields, content[MAX_PLAYER_NAME]; // Deklaration der benötigten Variablen (content muss max. so groß wie ein Username sein).
cache_get_data(rows, fields); // Die Anzahl der Reihen und Spalten der Ergebnismenge herauslesen und abspeichern.
for(new i = 0; i != rows; i++) { // Schleife die Anzahl der Reihen (= Anzahl der Häuser) durchlaufen lassen.
cache_get_row(i, 0, content); // Daten einer Zeile im String 'content' speichern.
hausInfo[i][hID] = strval(content); // HausArray mit Enumerator mit dem Integer (strval()) füllen.
cache_get_row(i, 1, content); // Wiederholung für andere Werte ...
hausInfo[i][hPreis] = strval(content);
cache_get_row(i, 2, content); // Feld steigt nach und nach wie auch im Query von links nach rechts.
format(hausInfo[i][hBesitzer], MAX_PLAYER_NAME, "%s", content);
cache_get_row(i, 3, content);
hausInfo[i][hInterior] = strval(content);
cache_get_row(i, 4, content);
hausInfo[i][hX] = floatstr(content);
cache_get_row(i, 5, content);
hausInfo[i][hY] = floatstr(content);
cache_get_row(i, 6, content);
hausInfo[i][hZ] = floatstr(content);
}
printf("Haussystem: Es wurden %i Häuser geladen.", rows); // Ausgabe der Anzahl der geladenen Häuser.
return 1;
}
Um den Vorgang des Ladens der Häuser zu vollenden fehlt noch der letzte Schritt, nähmlich der eigentlichen Generierung der Häuser auf der Karte. Wir erweitern unseren Callback also um eine Funktion, welche uns die Daten in Praktisches umwandeln soll und erweiteren unseren Enumerator um zwei Halter, welche jedoch nicht in der Datenbank abzuspeichern sind. Ebenfalls definieren wir am Kopf des Scriptes, wann ein String NULL ist:
#define HAUS_TEXT_COLOR (0xE2A31DFF) // Farbe des 3D-Text-Labels
#if !defined isnull
#define isnull(%1) \
((!(%1[0])) || (((%1[0]) == '\1') && (!(%1[1]))))
#endif
enum hausEnumerator {
hID, // Eindeutige ID des Hauses (Typ: INT)
hPreis, // Preis zum Erwerb des Hauses (Typ: INT)
hBesitzer[MAX_PLAYER_NAME], // Name des Besitzers des Hauses (Typ: VARCHAR ; Größe: MAX_PLAYER_NAME = 24)
hInterior, // Interior des Hauses (nicht die InteriorID - mehr dazu später!) (Typ: INT)
Float:hX, // X-Koordinate des Hauseingangs (Typ: FLOAT)
Float:hY, // Y-Koordinate des Hauseingangs (Typ: FLOAT)
Float:hZ, // Z-Koordinate des Hauseingangs (Typ: FLOAT)
hCpID, // CheckpointID (Nicht in Datenbank abzuspeichern!)
Text3D:h3DText // ID des 3DText-Labels (Nicht in Datenbank abzuspeichern!)
};
public OnGameModeLoadHouses() {
new rows, fields, content[MAX_PLAYER_NAME]; // Deklaration der benötigten Variablen (content muss max. so groß wie ein Username sein).
cache_get_data(rows, fields); // Die Anzahl der Reihen und Spalten der Ergebnismenge herauslesen und abspeichern.
for(new i = 0; i != rows; i++) { // Schleife die Anzahl der Reihen (= Anzahl der Häuser) durchlaufen lassen.
cache_get_row(i, 0, content); // Daten einer Zeile im String 'content' speichern.
hausInfo[i][hID] = strval(content); // HausArray mit Enumerator mit dem Integer (strval()) füllen.
cache_get_row(i, 1, content); // Wiederholung für andere Werte ...
hausInfo[i][hPreis] = strval(content);
cache_get_row(i, 2, content); // Feld steigt nach und nach wie auch im Query von links nach rechts.
format(hausInfo[i][hBesitzer], MAX_PLAYER_NAME, "%s", content);
cache_get_row(i, 3, content);
hausInfo[i][hInterior] = strval(content);
cache_get_row(i, 4, content);
hausInfo[i][hX] = floatstr(content);
cache_get_row(i, 5, content);
hausInfo[i][hY] = floatstr(content);
cache_get_row(i, 6, content);
hausInfo[i][hZ] = floatstr(content);
CreateHouseOnMap(i); // Übergabe des Indexes im Array zur einfacheren Handhabung.
}
printf("Haussystem: Es wurden %i Häuser geladen.", rows); // Ausgabe der Anzahl der geladenen Häuser.
return 1;
}
stock CreateHouseOnMap(hausID) {
// Erstellen eines neuen Checkpoints am Eingang des Hauses. Die CheckpointID wird im Array gespeichert.
hausInfo[hausID][hCpID] = CreateDynamicCP(hausInfo[hausID][hX], hausInfo[hausID][hY], hausInfo[hausID][hZ], 1.5);
new labelText[70]; // Erstellen eines neuen Strings zur Formatierung eines 3D-Text-Labels.
if(isnull(hausInfo[hausID][hBesitzer])) { // Falls das Haus noch keinen Besitzer hat (dann ist der String NULL) ...
format(labelText, sizeof(labelText), "- Dieses Haus ist zu kaufen! -\nPreis: $%i", hausInfo[hausID][hPreis]); // ... setze Text.
} else { // Falls doch ...
format(labelText, sizeof(labelText), "- Dieses Haus ist in Besitz! -\nBesitzer: %s", hausInfo[hausID][hBesitzer]); // ... setze Text.
}
// Erstellen des 3D-Text-Labels mithilfe des Streamers. Ebenfalls wird der Text in die Mitte des Checkpoints gesetzt und die Streamdistance auf 5 gesetzt,
// sodass der Text nur in unmittelbarer Nähe zu sehen ist.
hausInfo[hausID][h3DText] = CreateDynamic3DTextLabel(labelText, HAUS_TEXT_COLOR, hausInfo[hausID][hX], hausInfo[hausID][hY], hausInfo[hausID][hZ] - 0.3, 5);
return 1;
}
Ganz offensichtlich verwendest du ein falsches MySQL-Plugin. Ich denke du verwendest R5/R6 Includes und ein R7 Plugin.
Warum sind dann alle Helikopter betroffen? Du weißt lspdcars[29] -eine- VehicleID zu, welche wir testen. Es kann keine doppelten VehicleIDs geben, daher kann die VehicleID als Schlüssel fungieren.
Was ist wenn ich eigentlich dini benutze, für einen großen Server?
Was ist eigentlich genau gemeint mit es "lädt" schneller?
Dini ist nicht performancestark - Gründe wurden oben genannt. Der Grund, den DMA nun aufgearbeitet hat ist, dass in einer normalen Datei alle Zeichen 1 Byte groß sind. So verbraucht 286162 zB. 6 Byte (Siehe: http://www.torsten-horn.de/techdocs/ascii.htm). Nun komprimiert er die Zeichen, wodurch sie nicht mehr menschenlesbar sind, spart dadurch jedoch Bytes, die er nicht speichern bzw. laden muss. Résumé: Zeitersparnis.
Es wird gesagt, dass AddStaticVehicleEx (http://wiki.sa-mp.com/wiki/AddStaticVehicleEx) die VehicleID zurückgibt. Also können wir diese überprüfen:
// Beispiel, wie man es in einem Motorbefehl verwenden könnte.
if(GetPlayerVehicleID(playerid) == lspdcars[29] && fraktionsRank(playerid) < 5) {
// Spieler ist nicht befugt, dieses Fahrzeug zu benutzen.
}