CSmMode, MapEditorPlugin, CServerPlugin or CManiaApp (though we don’t have access to create new ones)CMlScript and all inherited classes from this CSmMlScriptIngameFirst you need to know that the script of the game mode and the Manialink defined inside game mode are two separate things (usually layers) - which don’t communicate with each other by default. The game mode script is run on the server while the Manialink you define in it is send and read locally on the computer of the player.
To make this work you first need to define the netwrite-part or “output” of the variable at game mode! To address singular player(s) you can define the variable for UI and incase you wish to send it to everyone in a team: for Teams[0]
Teams[0]will be defined always, even on TA-based modes. on team based modes you can address different teams (0 to N) or useTeams[0]for generic all.
netread/write variables should be always be prefixed with Net_. It is a convention to know the varible is indeed network based and to overcome naming conflicts between normal and net variables - you can’t have same named netread/write variable and normal ones.
Send from Backend (CSmMode)
// #RequireContext CSmMode
// to address a singular player
declare UI <=> UIManager.GetUI(Player);
declare netwrite Text Net_Variable for UI;
// to address all players
declare netwrite Text Net_Variable for Teams[0];
// Send
Net_Variable = "new value";
Receive at Client
You have to create an “input” variable on the side where you will receive the value of the other part (for example from the script to the variable) with the netread parameter between the declaration and the type of the variable.
You can not manipulate the value of a netread variable, if you do, the script will crash!
<manialink version="3">
<label id="debug" text="n/a" />
<script><!--
//#RequireContext CSmMlScriptIngame
main() {
let Label = Page.GetFirstChild("debug") as CMlLabel;
if(UI != Null) {
declare netread Integer Net_MyVariable for UI;
while(True) {
Label.Value = "Value: " ^ Net_MyVariable;
yield;
}
}
}
--></script>
</manialink>
Note: Bots doesn’t have an UI, so check if UI exists for the player object:
if (UI != Null) {
// do things
}
There is two ways to send data back from game clients, using Maniascript SendCustomEvent()-method or usign Manialink scriptaction=-attribute on elements that support it: <quad/> or <label/>.
Note: you can send and receive only
Textvalues.
Send from client ManiaScript
<manialink version="3" name="testing">
<script><!--
// #RequireContext CSmMlScriptIngame
SendCustomEvent("MyEvent", ["MyData"]);
--></script>
</manialink>
Send from client Manialink
To send from game client Manialink without needing to use maniascript, you can use xml attribute scriptaction=.
Format for scriptaction: eventName'value1'value2'value3'value4'…
<manialink version="3" name="testing">
<label pos="0 0" size="15 5" text="Send"
focusareacolor1="999" focusareacolor2="3ad"
scriptaction="MyEvent'Mydata" />
</manialink>
Receive at backend (CSmMode)
Receiving the values is handled by processing PendingEvents of UIManager:
for example:
// #RequireContext CSmMode
while(True) {
yield;
for (UIEvent in UIManager.PendingEvents) {
if (UIEvent.Type == CUIConfigEvent::EType::OnLayerCustomEvent) {
if (UIEvent.CustomEventType == "MyEvent") {
log("Received MyEvent");
log(UIEvent.CustomEventData);
}
}
}
}
Send from Controller
Different controllers has their own ways of communicating the XMLRPC interface. Here is generic pseudo code how to make a call, in this example
xmlrpcis your xmlrpc client.
xmlrpc.call("TriggerModeScriptEvent", "MyEvent", "MyValue");
// or
xmlrpc.call("TriggerModeScriptEventArray", "MyCustomEvent", ["MyCustomValue"]);
Receive at Backend (CSmMode)
// #RequireContext CSmMode
while(True) {
yield;
for (XEvent in XmlRpc.PendingEvents) {
if (XEvent.Type == CXmlRpcEvent::EType::Callback) {
if (XEvent.Param1 == "MyEvent") {
log("MyEvent Reached");
log(XEvent.Param2);
}
}
if (XEvent.Type == CXmlRpcEvent::EType::CallbackArray) {
if (Xevent.ParamArray1 == "MyCustomEvent") {
log("CustomEvent Reached");
log(XEvent.ParamArray2);
}
}
}
}
Send from Backend (CSmMode)
// #RequireContext CSmMode
XmlRpc.SendCallback("MyCallback", "MyValue");
XmlRpc.SendCallbackArray("MyCallbackArray", ["CallbackValue"]);
Receiving at XMLRPC controller
Different controllers has their own ways of communicating the XMLRPC interface. Here is generic pseudo code how to make a call, in this example
xmlrpcis your xmlrpc client.
xmlrpc.addListener("MyCallback");
xmlrpc.addListener("MyCallbackArray");
xmlrpc.on("MyCallback", (value) => {
console.log(value);
});
xmlrpc.on("MyCallbackArray", (value) => {
console.log(value);
});
Sending from client Maniascript*
<?xml version="1" charset="UTF-8"?>
<manialink id="1" name="test" version="3">
<script><!--
// #RequireContext CSmMlScriptIngame
TriggerPageAction("MyAction");
--></script>
</manialink>
Send from client Manialink
<?xml version="1" charset="UTF-8"?>
<manialink id="1" name="test" version="3">
<label pos="0 0" size="15 5" text="Send" focusareacolor1="999" focusareacolor2="3ad"
action="MyAction"
/>
</manialink>
Receive at controller
Different controllers has their own ways of communicating the XMLRPC interface. Here is generic pseudo code how to make a call, in this example
xmlrpcis your xmlrpc client.
xmlrpc.addListener("PlayerManialinkPageAnswer");
xmlrpc.on("PlayerManialinkPageAnswer", (uid, login, answer, entries) => {
console.log("received manialink action:");
console.log(login, answer, entries);
});
There is a tool that can be useful in ManiaScript to count the amount of data exchanged between server and clients.
log(Dbg_DumpDeclareForVariables(Teams[0], False));
This function return a big text with every network variable in the Nod you put in, can be Teams[0], Teams[1], This, UI, etc…
You will see the number of variables and total size on top and then one line of details per variables.
Take in mind that more data you will send to clients through network, more it will use the bandwidth and can cause lag on client and server side.
Example of return on a gamemode (not full, too much data) :
netwrite: 78 values (9494 bytes)
86: Text[] Net_LibUI3_Modules = [???]
32: Integer Net_LibUI3_ModulesUpdate = 189
40: Integer Net_LibRaceWarmUp_WarmUpPlayedNb = 1
40: Integer Net_LibRaceWarmUp_WarmUpDuration = 1
45: Boolean Net_Race_WarmupHelpers_IsWarmupActive = False
41: Boolean Net_LibRaceWarmUp_LayerVisibility = True
45: Integer Net_LibRaceWarmUp_LayerPositionUpdate = 189
47: Vec3 Net_LibRaceWarmUp_LayerPosition = <153., 0., 0.>
43: Boolean Net_SplitScreen_PauseMenu_IsVisible = True
44: Boolean Net_Race_PauseMenuOnline_IsLocalMode = False
56: Boolean Net_Race_PauseMenuOnline_IsPreviousReplayAllowed = False
88: Text Net_Race_PauseMenuOnline_Help_Description = "$lhttps://trackmania-formula-league.com"
35: Integer Net_Race_Record_SetupUpdate = 17
38: Boolean Net_Race_Record_PBGhostEnabled = False
35: Boolean Net_Race_Record_CelebratePB = False
36: Boolean Net_Race_Record_MedalEnabled = False
38: Boolean Net_Race_Record_CelebrateMedal = False
35: Text Net_Race_Record_ScopeSeason = ""
38: Text Net_Race_Record_ScopeNotSeason = ""
32: Text Net_Race_Record_ModeName = ""
34: Text Net_Race_Record_CustomData = ""
38: Boolean Net_Race_Record_DisplayRecords = False
41: Boolean Net_Race_Record_NewRecordsEnabled = False