In deze tutorial ga ik uitleggen hoe je nieuwe opcodes kan maken voor in je CLEO scripts. Deze tutorial is gemaakt omdat er weinig informatie over te vinden is, en kennis moet je delen.Deze tutorial is gericht op mensen met kennis van programmeren en die van GTA modden houden .
Benodigdheden
Grand theft auto 3, VC of SA.
In deze tutorial maak ik gebruik van San Andreas, echter moet dit werken voor bovengenoemde versies.
Ervaring met programmeren, bij voorkeur C en/of C++.
Ervaring met de scripttaal SCM.
Voorbereidingen
Zorg ervoor dat je:
Een van bovenstaande GTA versies heb geïnstalleerd
CLEO 4 hebt geïnstalleerd
Sannybuilder hebt geïnstalleerd
Visual Studio hebt geïnstalleerd
CLEO 4 SDK geinstalleerd is.
Informatie omtrent CLEO opcodes
CLEO opcodes verschillen van de standaard opcodes die het spel zelf gebruikt. De standaard opcodes van het spel die kan je zowel vanuit SCM scripts en CLEO scripts aanroepen, CLEO opcodes daarentegen kan je enkel aanroepen wanneer je CLEO geïnstalleerd hebt, en deze dus beschikbaar is.CLEO opcodes die roepen een functie aan in een externe bibliotheek, deze externe bibliotheken kan je vinden in je CLEO folder, deze hebben de extensie .CLEO. De .CLEO bestanden zijn in feite gewone dll’s en bevatten de code die uitgevoerd wordt wanneer de desbetreffende opcode in een script aangeroepen wordt.
Project template maken
We gaan nu het project voor de opcode maken, hier komt de code te staan die bij je zelfgemaakte opcode hoort.
New project
Als eerste gaan we Visual Studio opstarten. Hierin gaan we naar: File > new > Project. In het linker deelvenster kies je: Templates > Visual C++ > Win32 Project. Vul onder een naam voor het project en voor de solution in.In deze tutorial hanteer ik de projectnaam: cleo_opcode_tutorial en de soluction naam: cleoOpcodes. Druk op Ok, een venster verschijnt, druk hier op next. Selecteer nu onder Application type: DLL. Onder Additional options vink je het vakje Empty project aan en druk op Finish.
Project properties
Voordat we met het project aan de slag gaan, moeten we eerst enkele zaken instellen. Druk met de rechtermuisknop op het project (dus niet de solution) en selecteer Properties. Ga nu naar onderstaande en pas aan:
Configuration Properties > General > Project Defaults > Character Set
Pas de Character Set aan naar: Not Set.
Configuration Properties > C/C++ > General > Additional Include Directories
Druk hier op edit (uitklappen eerst) Vul hier het pad in waar je de CLEO_SDK map hebt staan.
Configuration Properties > C/C++ > Advanced > Compile As
Kies hier of je een C of C++ plugin gaat maken. In dit deel van de tutorial wordt gebruikt gemaakt van C++.
Configuration Properties > Linker > General > Output file
Hier kan je aanpassen waar de DLL komt te staan na het compilen. Het is net zo makkelijk dat hij meteen de juiste extensie heeft. Het pad wat je er neer zet, kan relatief zijn aan je huidige projectmap. Ik kies ervoor om in de rootmap van de solution een map te maken die cleo_plugins heet. Dus in mijn geval staat er bij Output file: ..\cleo_plugins\$(ProjectName).cleo. Als je wilt, kan je er ook voor zorgen dat het meteen in je GTA SA CLEO map staat, aan jou de keus
Zoals je misschien al ziet in dllmain.cpp, wordt initOpcodes() in opcodes.cpp aangeroepen wanneer de dll succesvol aan het proces verbonden zit.In de initOpcodes kunnen we nu onze eigen opcode registreren.
CLEO Specifieke code
Voordat we onze eigen opcode kunnen registreren, is het van belang om te weten welke types we moeten gebruiken, en hoe een opcode geregistreerd wordt. Ook zou het reuze handig zijn als we weten hoe je code communiceert met de variabelen in je CLEO script.
Registreren van een opcode
Elke opcode heeft een indentifier. De indentifier voor eigengemaakte cleo opcodes heeft een bereik van 0AF0 t/m 7FFF.Elke opcode die je maakt moet een unieke indentifier hebben. Ook heeft elke opcode een callback nodig, de callback is de functie in de dll die bij je opcode hoort, deze declareren we op de volgende manier:
Met bovenstaand is je opcode geregistreerd en kan je beginnen met je eigen code te schrijven.
Communiceren met het script
Een belangrijk, en onmisbaar onderdeel is het verkrijgen en verzenden van waardes van variabelen tussen je plugin en een CLEO script. Hiervoor zijn enkele CLEO specifieke functies:
Voor het doorgeven van een string waarde naar de eerst volgende parameter.
CLEO_GetIntOpcodeParam(CScriptThread* thread)
Voor het lezen van een integer van de eerst volgende parameter.
CLEO_GetFloatOpcodeParam(CScriptThread* thread)
Voor het lezen van een float waarde van de eerst volgende parameter.
CLEO_ReadStringOpcodeParam(CScriptThread* thread, LPSTR buf, int size);
Voor het lezen van een tekenreeks vanaf de eerst volgende parameter.
Er is een mogelijkheid om alle variabelen die in huidig opcode gebruikt worden in een keer op te halen, dit kan tot maximaal 32 variabelen.
CLEO_RetrieveOpcodeParams(CScriptThread* thread, int count)
Hiermee worden het aantal opgegeven parameters van de huidige opcode gelezen en in de CLEO variabel opcodeParams gezet. Een voorbeeld om deze te gebruiken:
float x, y, z;
CLEO_RetrieveOpcodeParams(thread, 3);
x = opcodeParams[0].fParam; y = opcodeParams[1].fParam; z = opcodeParams[2].fParam;
CLEO_RecordOpcodeParams(CScriptThread* thread, int count)
Hiermee kan je waardes in de CLEO variabel opcodeParams zetten.
Overige CLEO functies
CLEO kent naast de register en lees/schrijf functies ook nog enkele andere, deze staan hieronder beschreven.
CLEO_GetVersion()
Hiermee kun je de huidige CLEO versie achterhalen.
CLEO_GetGameVersion()
Hiermee kan je de huidige GTA versie achterhalen, de waardes waarmee het vergeleken kan worden zijn:
- GV_US10: Amerikaanse versie 1.0;.
- GV_US11: Amerikaanse versie 1.01, deze wordt niet ondersteund door CLEO.
- GV_EU10: Europese versie 1.0.
- GV_EU11: Europese versie 1.01.
- GV_UNK: Onbekende versie
CLEO_GetOperandType(CScriptThread* thread);
Hiermee kan je bepalen wat voor type de waarde van de eerst volgende parameter is.
Mogelijke types zijn:
globalVar: Type: variabel (bijvoorbeeld $PLAYER_ACTOR)
Hiermee sluit je de aangeroepen functie van de opcode af en je geeft hier aan of het succesvol was of niet. Dit kan handig zijn als bevestiging als je verder geen waardes teruggeeft aan het script.
CLEO_SkipOpcodeParams(CScriptThread* thread, int count)
Met deze functie kan je het aangegeven aantal parameters overslaan.
CLEO_ThreadJumpAtLabelPtr(CScriptThread* thread, int labelPtr)
Met deze functie zal je CLEO script naar de aangegeven geheugenlocatie springen, om hier het script voort te zetten.
Een opcode maken
Met alle bovenstaande informatie kunnen we nu beginnen aan het maken van onze eigen opcode.Om het in deze tutorial kort en simpel te houden, zal ik een simpele rekensom doen en deze terugschrijven naar de parameter.Voordat we nu echt de opcode kunnen gaan declareren, moeten we eerst bedenken welke indentifier hij krijgt. Als indentifier kies ik voor de waarde: 0B30.
Opcode declareren en registeren
Wat ikzelf fijn vind om te doen is in opcodes.cpp na de includes de indentifiers van de variabelen neer te zetten:
#define OPCODE_TUTORIAL 0x0B30
Onder de opcode indentifiers is het makkelijk om de callback(s) te definiëren.
Nu moeten we gaan verzinnen wat voor parameters deze opcode heeft, welke parameters zijn om uit te lezen, en welke om naar toe te schrijven, en wat voor type is die parameter?Als voorbeeld ga ik een opcode maken waar je een getal opgeeft, deze wordt vermenigvuldigd met het tweede getal en het resultaat hiervan wordt weggeschreven naar het script. Nu moeten we gaan nadenken over de volgorde hoe we alles in willen gaan lezen.Ik wil dat mijn opcode straks op de volgende manier in een cleo script gebruikt wordt:
0@ = tutorial_opcode_1_vermenigvuldigd 1@ met 2@
Als we bovenstaande nemen dan weten we het resultaat van 0@ pas als we eerst 1@ en 2@ hebben ingelezen, dat is dus ook precies de volgorde hoe we dat gaan doen.Als we wiskundige berekeningen op een variabel willen loslaten, dan is het van belang dat we zeker weten dat de variabel geldig is. Laten we bovenstaande dus gaan implementeren:
OpcodeResult WINAPI Script_Tutorial_opcode(CScriptThread* thread)
{
BOOL result = FALSE;
int varA = 0, varB = 0, varC = 0;
//Lees 1e parameter uit
switch (CLEO_GetOperandType(thread))
{
case globalVar:
case localVar:
case globalArr:
case localArr:
case imm8:
case imm16:
case imm32:
varA = CLEO_GetIntOpcodeParam(thread);
result = TRUE;
break;
default:
CLEO_SkipOpcodeParams(thread, 1);
result = FALSE;
}
//Lees 2e parameter uit
switch (CLEO_GetOperandType(thread))
{
case globalVar:
case localVar:
case globalArr:
case localArr:
case imm8:
case imm16:
case imm32:
varB = CLEO_GetIntOpcodeParam(thread);
result = TRUE;
break;
default:
CLEO_SkipOpcodeParams(thread, 1);
result = FALSE;
}
//Kloppen beide?
if (result)
{
varC = (varA * varB);
CLEO_SetIntOpcodeParam(thread, varC); //Schrijf de waarde naar de 3e parameter
}
else
{
CLEO_SetIntOpcodeParam(thread, -1); //Schrijf de waarde naar de 3e parameter
}
return OR_CONTINUE;
}
Bovenstaande code is inefficiënt (dubbele switch), maar het gaat erom dat het idee duidelijk is.Wanneer je bovenstaande hebt gevolgd, kan je nu je plugin compilen.Wanneer je plugin gecompiled is, moet je deze in de CLEO map van je GTA zetten.
Sannybuilder compiler
De volgorde van op welke manier de parameters ingelezen/geschreven worden wordt bepaald door de compiler van Sannybuilder. De compiler beslist dit aan de hand van de configuratiebestanden.
Navigeer naar de map waar je Sannybuilder geïnstalleerd hebt. In deze map vind je een map: data.In de map data staat voor elke GTA die Sannybuilder ondersteund een map. In mijn geval is het SanAndreas, de map sa dus.De bestanden die aangepast moeten worden zijn:
Opcodes.txt
SASCM.ini (voor GTA Vice City is het: VICESCM.ini, voor GTA 3 is het SCM.ini)
opcodes.txt
Het bestand opcodes.txt is simpelweg het bestand waarin de opcodes staan zoals je ze in een SCM/CLEO script zou gebruiken. Het formaat van een opcode in dit bestand is:
[opcode indentifier]: [tekst en parameters]
Ik heb besloten dat mijn opcode er op de volgende manier uit wilt laten zien:
0@ = tutorial_opcode_1_vermenigvuldig 1@ met 2@
Hier hoeven we enkel nog de indentifier voor te zetten met een dubbelepunt, het volgende komt dus onderaan in opcodes.txt te staan:
0B30: 0@ = tutorial_opcode_1_vermenigvuldig 1@ met 2@
SCM.ini
De SCM.ini bestanden zijn de configuratiebestanden waarin de opcodes gedefinieerd staan zoals Sannybuilder ze toepast. Het formaat om een opcode te declareren is:
[opcode indentifier]=[aantal parameters],[tekst en parameters]
De parameters kan je gewoon tussen de tekst door zetten op welke plek wat je maar wilt.Waar je wel op moet letten is de index van de parameter, deze bepaald de volgorde van hoe de parameters afgehandeld worden. De index geef je aan met %[index] (natuurlijk zonder de [ ]).Daarnaast moet je ook het type aangeven, deze komt direct achter de index.
De volgende types zijn er:
d% = kan elk type zijn
p% = pointer
o% = object model (alle types)
g% = gxt referenties, 8 tekens lang
x% = extern script
Open het bestand SASCM.ini, en scrol volledig naar onder.We kunnen bijna hetzelfde erin zetten, als in opcodes.txtAls eerste zetten we dus de indentifier neer: 0B30 (zie bovenstaande code)Zoals ik al aangaf, wil ik dat mijn opcode er op de volgende manier uit komt te zien:
0@ = tutorial_opcode_1_vermenigvuldig 1@ met 2@
De parameters zijn allemaal integers, deze worden dus vervangen met een type d%.Omdat we willen dat 1@ het eerste uitgelezen wordt, 2@ als tweede en 0@ als derde, moeten we de index hiervan correct aangeven.
In het formaat wat ik aangaf, zou mijn opcode in het SCM.ini bestand er zo uit zien:
0B30=3,%3d% = tutorial_opcode_1_vermenigvuldig %1d% met %2d%
Voer bovenstaande uit en sla opcodes.txt en de SCM.ini op. Indien je Sannybuilder had opgestart, moet je deze opnieuw opstarten.
Gebruiken in een CLEO script
Om de opcode te gebruiken in een CLEO script, hoeven we simpelweg enkel een script te maken en de opcode hierin te gebruiken zoals wel met elke andere opcode zouden doen:
{$CLEO .cs}
thread 'OPCODE_TUT'
:INIT
1@ = 8
2@ = 5
3@ = 0
:OPCODE_TUT
wait 50
if
0AB0: key_pressed 0x4F // Toets O
then
if
0AB0: key_pressed 0x11 // Toets Ctrl
then
gosub @EXECUTE_NEW_OPCODE
end
end
jump @OPCODE_TUT
:EXECUTE_NEW_OPCODE
wait 0
0B30: 3@ = tutorial_opcode_1_vermenigvuldig 1@ met 2@
0ACE: show_formatted_text_box "Opcode resultaat: %d" 3@
wait 1000
return
0A93: end_custom_thread
Zoals je ziet: wanneer er op de knoppen: CTRL + O gedrukt wordt, springt het script naar label :EXECUTE_NEW_OPCODE, hier staat de gloednieuwe opcode die uitgevoerd wordt.
Testen
En als laatste stap, starten we het spel op en gaan we het testen.
Tot slot
Bovenstaande informatie kan mogelijk enkele fouten bevatten, wanneer je een fout ziet, meld het dan a.u.b. zodat ik het kan aanpassen. Als je opmerkingen of vragen hebt, post ze gewoon
Opcodes maken voor gebruik in CLEO scripts
Inhoud
1. Inleiding
2. Benodigdheden
3. Voorbereidingen
4. Informatie omtrent CLEO opcodes
5. Project template maken
6. Cleo specifieke code
7. Een opcode maken
8. Sannybuilder compiler
9. Gebruiken in een CLEO script
10. Testen
11. Tot slot
Inleiding
In deze tutorial ga ik uitleggen hoe je nieuwe opcodes kan maken voor in je CLEO scripts. Deze tutorial is gemaakt omdat er weinig informatie over te vinden is, en kennis moet je delen.Deze tutorial is gericht op mensen met kennis van programmeren en die van GTA modden houden
.
Benodigdheden
Voorbereidingen
Zorg ervoor dat je:
Informatie omtrent CLEO opcodes
CLEO opcodes verschillen van de standaard opcodes die het spel zelf gebruikt. De standaard opcodes van het spel die kan je zowel vanuit SCM scripts en CLEO scripts aanroepen, CLEO opcodes daarentegen kan je enkel aanroepen wanneer je CLEO geïnstalleerd hebt, en deze dus beschikbaar is.CLEO opcodes die roepen een functie aan in een externe bibliotheek, deze externe bibliotheken kan je vinden in je CLEO folder, deze hebben de extensie .CLEO. De .CLEO bestanden zijn in feite gewone dll’s en bevatten de code die uitgevoerd wordt wanneer de desbetreffende opcode in een script aangeroepen wordt.
Project template maken
We gaan nu het project voor de opcode maken, hier komt de code te staan die bij je zelfgemaakte opcode hoort.
New project
Als eerste gaan we Visual Studio opstarten. Hierin gaan we naar: File > new > Project. In het linker deelvenster kies je: Templates > Visual C++ > Win32 Project. Vul onder een naam voor het project en voor de solution in.In deze tutorial hanteer ik de projectnaam: cleo_opcode_tutorial en de soluction naam: cleoOpcodes. Druk op Ok, een venster verschijnt, druk hier op next. Selecteer nu onder Application type: DLL. Onder Additional options vink je het vakje Empty project aan en druk op Finish.
Project properties
Voordat we met het project aan de slag gaan, moeten we eerst enkele zaken instellen. Druk met de rechtermuisknop op het project (dus niet de solution) en selecteer Properties. Ga nu naar onderstaande en pas aan:
We zijn nu klaar met de project properties instellen.
Default files
Voor elk CLEO plugin project wat je maakt zijn er enkele bestanden vereist, ook is het handig is je gewoon elke keer dezelfde structuur gebruikt:
Zoals je misschien al ziet in dllmain.cpp, wordt initOpcodes() in opcodes.cpp aangeroepen wanneer de dll succesvol aan het proces verbonden zit.In de initOpcodes kunnen we nu onze eigen opcode registreren.
CLEO Specifieke code
Voordat we onze eigen opcode kunnen registreren, is het van belang om te weten welke types we moeten gebruiken, en hoe een opcode geregistreerd wordt. Ook zou het reuze handig zijn als we weten hoe je code communiceert met de variabelen in je CLEO script.
Registreren van een opcode
Elke opcode heeft een indentifier. De indentifier voor eigengemaakte cleo opcodes heeft een bereik van 0AF0 t/m 7FFF.Elke opcode die je maakt moet een unieke indentifier hebben. Ook heeft elke opcode een callback nodig, de callback is de functie in de dll die bij je opcode hoort, deze declareren we op de volgende manier:
Nu we een callback hebben kunnen we de opcode indentifier hieraan koppelen, dit doen we met:
Met bovenstaand is je opcode geregistreerd en kan je beginnen met je eigen code te schrijven.
Communiceren met het script
Een belangrijk, en onmisbaar onderdeel is het verkrijgen en verzenden van waardes van variabelen tussen je plugin en een CLEO script. Hiervoor zijn enkele CLEO specifieke functies:
Er is een mogelijkheid om alle variabelen die in huidig opcode gebruikt worden in een keer op te halen, dit kan tot maximaal 32 variabelen.
Overige CLEO functies
CLEO kent naast de register en lees/schrijf functies ook nog enkele andere, deze staan hieronder beschreven.
Een opcode maken
Met alle bovenstaande informatie kunnen we nu beginnen aan het maken van onze eigen opcode.Om het in deze tutorial kort en simpel te houden, zal ik een simpele rekensom doen en deze terugschrijven naar de parameter.Voordat we nu echt de opcode kunnen gaan declareren, moeten we eerst bedenken welke indentifier hij krijgt. Als indentifier kies ik voor de waarde: 0B30.
Opcode declareren en registeren
Wat ikzelf fijn vind om te doen is in opcodes.cpp na de includes de indentifiers van de variabelen neer te zetten:
Onder de opcode indentifiers is het makkelijk om de callback(s) te definiëren.
Nu de indentifiers en callback(s) bekend zijn, is het tijd om deze te registreren.
Callback implementatie: parameters
Nu de opcode(s) geregistreerd zijn kunnen we in de callback van elke opcode alles doen wat we willen. Zet het volgende onder de functie InitOpcodes():
Nu moeten we gaan verzinnen wat voor parameters deze opcode heeft, welke parameters zijn om uit te lezen, en welke om naar toe te schrijven, en wat voor type is die parameter?Als voorbeeld ga ik een opcode maken waar je een getal opgeeft, deze wordt vermenigvuldigd met het tweede getal en het resultaat hiervan wordt weggeschreven naar het script. Nu moeten we gaan nadenken over de volgorde hoe we alles in willen gaan lezen.Ik wil dat mijn opcode straks op de volgende manier in een cleo script gebruikt wordt:
Als we bovenstaande nemen dan weten we het resultaat van 0@ pas als we eerst 1@ en 2@ hebben ingelezen, dat is dus ook precies de volgorde hoe we dat gaan doen.Als we wiskundige berekeningen op een variabel willen loslaten, dan is het van belang dat we zeker weten dat de variabel geldig is. Laten we bovenstaande dus gaan implementeren:
Bovenstaande code is inefficiënt (dubbele switch), maar het gaat erom dat het idee duidelijk is.Wanneer je bovenstaande hebt gevolgd, kan je nu je plugin compilen.Wanneer je plugin gecompiled is, moet je deze in de CLEO map van je GTA zetten.
Sannybuilder compiler
De volgorde van op welke manier de parameters ingelezen/geschreven worden wordt bepaald door de compiler van Sannybuilder. De compiler beslist dit aan de hand van de configuratiebestanden.
Navigeer naar de map waar je Sannybuilder geïnstalleerd hebt. In deze map vind je een map: data.In de map data staat voor elke GTA die Sannybuilder ondersteund een map. In mijn geval is het SanAndreas, de map sa dus.De bestanden die aangepast moeten worden zijn:
opcodes.txt
Het bestand opcodes.txt is simpelweg het bestand waarin de opcodes staan zoals je ze in een SCM/CLEO script zou gebruiken. Het formaat van een opcode in dit bestand is:
Ik heb besloten dat mijn opcode er op de volgende manier uit wilt laten zien:
Hier hoeven we enkel nog de indentifier voor te zetten met een dubbelepunt, het volgende komt dus onderaan in opcodes.txt te staan:
SCM.ini
De SCM.ini bestanden zijn de configuratiebestanden waarin de opcodes gedefinieerd staan zoals Sannybuilder ze toepast. Het formaat om een opcode te declareren is:
De parameters kan je gewoon tussen de tekst door zetten op welke plek wat je maar wilt.Waar je wel op moet letten is de index van de parameter, deze bepaald de volgorde van hoe de parameters afgehandeld worden. De index geef je aan met %[index] (natuurlijk zonder de [ ]).Daarnaast moet je ook het type aangeven, deze komt direct achter de index.
De volgende types zijn er:
Open het bestand SASCM.ini, en scrol volledig naar onder.We kunnen bijna hetzelfde erin zetten, als in opcodes.txtAls eerste zetten we dus de indentifier neer: 0B30 (zie bovenstaande code)Zoals ik al aangaf, wil ik dat mijn opcode er op de volgende manier uit komt te zien:
De parameters zijn allemaal integers, deze worden dus vervangen met een type d%.Omdat we willen dat 1@ het eerste uitgelezen wordt, 2@ als tweede en 0@ als derde, moeten we de index hiervan correct aangeven.
In het formaat wat ik aangaf, zou mijn opcode in het SCM.ini bestand er zo uit zien:
Voer bovenstaande uit en sla opcodes.txt en de SCM.ini op. Indien je Sannybuilder had opgestart, moet je deze opnieuw opstarten.
Gebruiken in een CLEO script
Om de opcode te gebruiken in een CLEO script, hoeven we simpelweg enkel een script te maken en de opcode hierin te gebruiken zoals wel met elke andere opcode zouden doen:
Zoals je ziet: wanneer er op de knoppen: CTRL + O gedrukt wordt, springt het script naar label :EXECUTE_NEW_OPCODE, hier staat de gloednieuwe opcode die uitgevoerd wordt.
Testen
En als laatste stap, starten we het spel op en gaan we het testen.
Tot slot
Bovenstaande informatie kan mogelijk enkele fouten bevatten, wanneer je een fout ziet, meld het dan a.u.b. zodat ik het kan aanpassen. Als je opmerkingen of vragen hebt, post ze gewoon
Bijlage: source bestanden
Bewerkt: door Crypteq