Closed Chromowolf closed 9 months ago
A1.
Why is the condition necessary?
Without condition, sending occurs every frame and player's APM will spike, which is not desirable in most cases. (ex) MSQC built-in mouse location sends only when mouse positions are actually changed. You don't need to update value if it still remains same.
A2. Pass multiple times or split values into multiple entries :P There are max limit of command queue(=Max active MSQC units) so you gonna need both methods and your own additional way to verify it to send big values flawlessly. Check how SCArchive does.
Thank you. Reading the code of SCA is really a challenge for me. But I'll try. I may need some hint later. :P
I tried splitting the 4-byte value to two u16 variables, like this: main.edd:
[main]
input: in.scx
output: out.scx
[main.eps]
[MSQC]
0x6D0F38, AtLeast, 0; val, sendlb : receivelb
0x6D0F38, AtLeast, 0; val, sendhb : receivehb
[eudTurbo]
main.eps:
const receivelb = PVariable();
const receivehb = PVariable();
const receive = [0, 0, 0, 0, 0, 0, 0, 0];
var sendlb = 0; // low 2 byte
var sendhb = 0; // high 2 byte
function onPluginStart() {
EUDRegisterObjectToNamespace("sendlb", sendlb);
EUDRegisterObjectToNamespace("sendhb", sendhb);
EUDRegisterObjectToNamespace("receivelb", receivelb);
EUDRegisterObjectToNamespace("receivehb", receivehb);
}
function afterTriggerExec() {
sendlb = dwread_epd(EPD(0x6D0F38)) % 0x10000; //low byte
sendhb = dwread_epd(EPD(0x6D0F38)) / 0x10000; //high byte
for (var i = 0; i < 8; i++) {
receive[i] = receivelb[i] + receivehb[i] * 0x10000; //combine
}
}
Seems OK? Is there a cleverer way?
q, m = div(a, b);
calculates a ÷ b
, returns quotient and remainder.
sendlb = dwread_epd(EPD(0x6D0F38)) % 0x10000; //low byte
sendhb = dwread_epd(EPD(0x6D0F38)) / 0x10000; //high byte
// ▼ These can be written as,
sendhb, sendlb = div(dwread(0x6D0F38), 0x10000);
Thank you. Now I'm using this technique to run a test: main.edd:
[main]
input: in.scx
output: out.scx
[main.eps]
[MSQC]
0x51CE8C, AtLeast, 0; val, sendlb : receivelb
0x51CE8C, AtLeast, 0; val, sendhb : receivehb
[eudTurbo]
main.eps:
const receivelb = PVariable();
const receivehb = PVariable();
const receive = PVariable();
var sendlb = 0; // low 2 byte
var sendhb = 0; // high 2 byte
function onPluginStart() {
EUDRegisterObjectToNamespace("sendlb", sendlb);
EUDRegisterObjectToNamespace("sendhb", sendhb);
EUDRegisterObjectToNamespace("receivelb", receivelb);
EUDRegisterObjectToNamespace("receivehb", receivehb);
}
function afterTriggerExec() {
const invSysTime = dwread_epd(EPD(0x51CE8C)); //Get inverse system time (local)
sendhb, sendlb = div(invSysTime, 0x10000); //sendlb, sendhb = dwbreak(invSysTime); also ok?
for (var i = 0; i < 8; i++) {
receive[i] = receivelb[i] + receivehb[i] * 0x10000;
}
const cp = getcurpl();
const localID = getuserplayerid();
setcurpl(localID);
const pp = StringBuffer(256);
pp.printfAt(0, "Game tick = {}", getgametick());
pp.printfAt(1, "invTime = {}, hex = 0x{}", invSysTime, hptr(invSysTime)); //invSysTime sent
pp.printfAt(2, "sendhb = 0x{}, sendlb = 0x{}", hptr(sendhb), hptr(sendlb));
pp.printfAt(3, "Receive = {}, hex = 0x{}", receive[localID], hptr(receive[localID]));//invSysTime received
pp.printfAt(4, "recvhb = 0x{}, recvlb = 0x{}", hptr(receivehb[localID]), hptr(receivelb[localID]));
pp.printfAt(5, "Time diff = {}", receive[localID] - invSysTime); // Time difference between send and receive
pp.printfAt(6, "ms per frame: {}, {}, {}, {}, {}, {}, {}", dwread_epd(-122787), dwread_epd(-122786), dwread_epd(-122785), dwread_epd(-122784), dwread_epd(-122783), dwread_epd(-122782), dwread_epd(-122781));
setcurpl(cp);
}
Then I tested the out.scx using single-player mode in the slowest game speed (167ms/frame). And got the following result:
Frame 2:
Frame 3:
Frame 4:
Frame 5:
Frame 6:
Frame 7:
Frame 8: (problematic)
Frame 9:
Frame 10:
...
Frame 20:
...
Frame 154:
Frame 155:
Frame 156:
Frame 157:
Frame 158: (problematic)
Frame 159:
...
Frame 308: (problematic)
...
Frame 458: (problematic)
(Q1) There are 500ms lag between "send" and "receive", indicating a lag of 3 frames. When I try the map in multiplayer mode, the lag becomes 4 frames. What makes this time lag? Is it because there is only 1 QCUnit and it has to send 2 times? Nonetheless I'm expecting a 2-frame lag only.
(Q2) There are strange values every 150 frames, (see frame 8, frame 158, frame 308). This is obviously because MSQC received/calculated a wrong value (0xFFFFFFFF, which is -1). For example, the received value in Frame158 should be the inverse system time of Frame155 (i.e. 0xF0913C5f). The actual received values (high byte and low byte) are both -1, which is weird. Why does MSQC fail to send correct value for every 150 frames?
(Q1) You don't need MSQC for singleplayer game so IMO this isn't proper test. Command queue has size limit so that's why I made additional condition mandatory to encourage to only send on demand. SC will silently fail to issue order when you issue too many command so queue is full. The delay is same amount of time between when you right click unit and when actual unit ordered to move.
(Q2) It is intended behavior that the value is set to -1 when no sending happens. Long time ago it was 0 but there was a problem, not able to distinguish between receiving 0 and no sending so it was changed to -1.
You don't need MSQC for singleplayer game so IMO this isn't proper test.
I'll do another test with multiple players in one game.
SC will silently fail to issue order when you issue too many command so queue is full.
Could you please briefly explain the command queue mechanism in starcraft? I've read (a little bit) source code of MSQC and also the QueueCommand functions in the eudplib, but cannot fully understand. I've learned data structures in programming and know the queue structure. I just don't know how big the queue size is and how often starcraft does "dequeue/pop". Say If I select one unit and use right click to issue move order to this unit once for each frame, will the queue eventually be full? If I add frequency, say, 2 clicks for each frame, will the queue eventually be full? If I use trigger action Order(..., Move, ...)
, will it add to the command queue?
My friend has made some maps in which he gave several "patrol" orders for many units using the action Order(..., Patrol, ...)
constantly (nearly every frame). And he noticed that sometimes the units randomly failed to execute the order, and just stood still. May this be related to the command queue?
It is intended behavior that the value is set to -1 when no sending happens. Long time ago it was 0 but there was a problem, not able to distinguish between receiving 0 and no sending so it was changed to -1.
I know this feature: If no sending, then send -1. But in my case, I'm constantly sending values every frame, (see my codes). I cannot figure out why the "no sending" occurs every 150 frames. This means SC silently fail to issue order for every 150 frames in this case? If I add some additional condition, preventing MSQC from running every frame, this "no sending" problem would be fixed?
Order action is irrelevant. It doesn't share local command. Remaining queue space is at 0x57F0D8 but it's better to say sending is fallible, no sending can occur anytime so you would better handle it. I don't think the delay between sending and receiving is constant and your test does not check the number of receiving so I can't say whether it is really sending was lost or receiving didn't occur yet.
your test does not check the number of receiving
How to check the number of receiving?
How to check the number of receiving?
Don't constantly send value during whole game. Set how many sending occurs by the number or time. Say sends 100 times and wait return values.
I tested again. 2 players in battle.net (P1 and P2), fastest speed, send value 10 times: main.edd
[main]
input: in.scx
output: out.scx
[main.eps]
[MSQC]
sendlb.AtLeast(1); val, sendlb : receivelb
sendhb.AtLeast(1); val, sendhb : receivehb
[eudTurbo]
[freeze]
freeze: 0
main.eps:
const localID = getuserplayerid();
const receivelb = PVariable();
const receivehb = PVariable();
const receive = PVariable();
var sendlb = 0; // low 2 byte
var sendhb = 0; // high 2 byte
function onPluginStart() {
EUDRegisterObjectToNamespace("sendlb", sendlb);
EUDRegisterObjectToNamespace("sendhb", sendhb);
EUDRegisterObjectToNamespace("receivelb", receivelb);
EUDRegisterObjectToNamespace("receivehb", receivehb);
}
function afterTriggerExec() {
const gametick = getgametick();
const invSysTime = dwread_epd(EPD(0x51CE8C)); //Get inverse system time (local)
sendlb = 0; //Set to 0
sendhb = 0; //Set to 0
if (gametick <= 11) { //send value when gametick is 2,3,4,5,6,7,8,9,10,11
sendhb, sendlb = div(invSysTime, 0x10000); //break dw to w
}
for (var i = 0; i < 8; i++) {
receive[i] = receivelb[i] + receivehb[i] * 0x10000;
}
const cp = getcurpl();
setcurpl(localID);
const pp = StringBuffer(256);
pp.printfAt(0, "Game tick = {}", gametick);
pp.printfAt(1, "invTime = {}, hex = 0x{}", invSysTime, hptr(invSysTime));
pp.printfAt(2, "sendhb = 0x{}, sendlb = 0x{}", hptr(sendhb), hptr(sendlb));
//receive P1:
pp.printfAt(3, "Receive[0] = {}, hex = 0x{}", receive[0], hptr(receive[0]));
pp.printfAt(4, "recvhb[0] = 0x{}, recvlb[0] = 0x{}", hptr(receivehb[0]), hptr(receivelb[0]));
//receive P2:
pp.printfAt(5, "Receive[1] = {}, hex = 0x{}", receive[1], hptr(receive[1]));
pp.printfAt(6, "recvhb[1] = 0x{}, recvlb[1] = 0x{}", hptr(receivehb[1]), hptr(receivelb[1]));
//receive P3:
pp.printfAt(7, "Receive[2] = {}, hex = 0x{}", receive[2], hptr(receive[2]));
pp.printfAt(8, "recvhb[2] = 0x{}, recvlb[2] = 0x{}", hptr(receivehb[2]), hptr(receivelb[2]));
pp.printfAt(9, "Time diff = {}", receive[localID] - invSysTime); // Time difference between send and receive
setcurpl(cp);
}
The result is the same. There is something wrong in Frame 8. The following is the screen shots for P1:
Frame 2:
Frame 3:
Frame 4:
Frame 5:
Frame 6:
Frame 7:
Frame 8: (problematic)
Frame 9:
Frame 10:
Frame 11:
Frame 12:
...
Frame 15:
Frame 16: (no more sendings since here)
Is this test clear enough to illustrust the "frame 8" problem?
Is basemap size 256x256?
It's 128x128. I'll attach you the files if needed
for (var i = 0; i < 8; i++) { receive[i] = receivelb[i] + receivehb[i] * 0x10000; }
QueueGameCommand
was failed silently because queue was full.
(dwread(0x57F0D8) - dwread(0x654AA0) = remaining queue space
)for (var i = 0; i < 8; i++) {
if(receivelb[i] != ~0 && receivehb[i] != ~0) {
receive[i] = receivelb[i] + receivehb[i] * 0x10000;
} else {
receive[i] = ~0;
}
}
It is not guaranteed to send nor receive both receivelb and receivehb at the same time. Also it is not guaranteed whether SC always process command queue at every gametick.
Thank you! These two points are crucial. I didn't even think of this possibility.
I made it simple this time. I tried not combining the receivehb
and receivelb
. I also restrained the condition.
main.eps:
const localID = getuserplayerid();
const receivelb = PVariable();
const receivehb = PVariable();
const receive = PVariable();
var sendlb = 0; // low 2 byte
var sendhb = 0; // high 2 byte
var gametick = getgametick();
function onPluginStart() {
EUDRegisterObjectToNamespace("gametick", gametick);
EUDRegisterObjectToNamespace("sendlb", sendlb);
EUDRegisterObjectToNamespace("sendhb", sendhb);
EUDRegisterObjectToNamespace("receivelb", receivelb);
EUDRegisterObjectToNamespace("receivehb", receivehb);
}
function afterTriggerExec() {
gametick = getgametick();
const invSysTime = dwread_epd(EPD(0x51CE8C)); //Get inverse system time (local)
sendhb, sendlb = div(invSysTime, 0x10000); //break dw to w
const cp = getcurpl();
setcurpl(localID);
const pp = StringBuffer(256);
pp.printfAt(0, "Game tick = {}", gametick);
pp.printfAt(1, "invTime = {}, hex = 0x{}", invSysTime, hptr(invSysTime));
pp.printfAt(2, "sendhb = 0x{}, sendlb = 0x{}", hptr(sendhb), hptr(sendlb));
//receive P1:
pp.printfAt(3, "recvhb[0] = 0x{}, recvlb[0] = 0x{}", hptr(receivehb[0]), hptr(receivelb[0]));
//receive P2:
pp.printfAt(4, "recvhb[1] = 0x{}, recvlb[1] = 0x{}", hptr(receivehb[1]), hptr(receivelb[1]));
//receive P3:
pp.printfAt(5, "recvhb[2] = 0x{}, recvlb[2] = 0x{}", hptr(receivehb[2]), hptr(receivelb[2]));
const commandDataEPD = EPD(0x654880); //Copied from qgc.py, not sure it's the correct address
const maxBufferSize = dwread_epd(EPD(0x57F0D8));
const bufferUsed = dwread_epd(EPD(0x654AA0));
pp.insert(0, "Command data =");
for (var i = 0; i < 7; i++)
pp.appendf(" {},", hptr(dwread_epd(commandDataEPD + i)));
pp.DisplayAt(6);
pp.printfAt(7, "Max buffer size = {}, buffer used = {}, remaining = {}", maxBufferSize, bufferUsed, maxBufferSize - bufferUsed);
setcurpl(cp);
}
When [MSQC] is:
[MSQC]
gametick.AtLeast(3); gametick.AtMost(4); val, sendlb : receivelb
gametick.AtLeast(3); gametick.AtMost(4); val, sendhb : receivehb
I got:
When [MSQC] is:
[MSQC]
gametick.Exactly(3); val, sendlb : receivelb
gametick.Exactly(3); val, sendhb : receivehb
I got:
When [MSQC] is:
[MSQC]
gametick.Exactly(4); val, sendlb : receivelb
gametick.Exactly(4); val, sendhb : receivehb
I got:
So the temporary conclusion is that: they are not received by the same time in this case. Seems that this weird situation happens with high probability in Frame8, Frame158, Frame308. Is there anything that MSQC can do to prevent/remedy this weird SC internal problem? Btw I don't know how to interpret the QueueGameCommand
In short: It cannot be guaranteed that I receive the 2 value at the same time, even if I constrain the condition to make it send the 2 values only ONCE.
Is there anything that MSQC can do to prevent/remedy this weird SC internal problem?
Once sharing feature is integrated in upcoming new language, send function will be fallible operation and end user would choose what to do based on result of send.
Or another queue could be added and let send function to enqueue (again it is fallible when queue is full).
Fixed MSQC regression, please re-download euddraft: https://github.com/armoha/euddraft/releases/tag/v0.9.1.4
/* .edd
[main]
input: basemap.scx
output: msqc_dbg.scx
[EUDTurbo]
[main.eps]
[MSQC]
send_switch.Exactly(1) ; val, send : receive
QCDebug: False
[freeze]
freeze: 0
*/
// test code
var send, send_switch;
const receive = PVariable();
const dbg = StringBuffer();
const me = getuserplayerid();
function onPluginStart() {
EUDRegisterObjectToNamespace("send", send);
EUDRegisterObjectToNamespace("send_switch", send_switch);
EUDRegisterObjectToNamespace("receive", receive);
}
function beforeTriggerExec() {
// send value once user press Q
static var is_q_pressed = false;
if (list(
is_q_pressed == false,
MemoryX(0x596A18 + 0x51, Exactly, 256, 256)
)) {
is_q_pressed = true;
// toggle send between 0 and 1
send_switch = true;
DoActions(send.AddNumberX(1, 1));
setcurpl(me);
dbg.printf("{0:c}{0:n} \x04sent \x07{1}", me, send);
} else if (list(
is_q_pressed == true,
MemoryX(0x596A18 + 0x51, Exactly, 0, 256)
)) {
is_q_pressed = false;
}
}
function afterTriggerExec() {
if (send_switch == true)
send_switch = false;
setcurpl(me);
foreach(human: EUDLoopPlayer("Human")) {
const my_receive = receive[human];
if (my_receive != -1) {
dbg.printf("{0:c}{0:n} \x04got \x07{1}", human, my_receive);
}
}
}
Thanks for the update! But are you sure you uploaded the correct MSQC.py? The newest MSQC.py is exactly the same file as that of euddraft0.8.9.9 Note: Line: 449
mrgn, strtb = chkt.getsection("MRGN"), TBL(chkt.getsection("STR"))
Newer version of MSQC:
try:
strtb = TBL(chkt.getsection("STR"))
except KeyError:
strtb = TBL(chkt.getsection("STRx"), load_entry=4, save_entry=4)
I copied wrong file soz! Editted release just now.
Now it works! Thx a lot. More questions (and nitpicks) about MSQC:
(1)
Why is Valkyrie the default QCUnit? All the QCUnits are invisible and cannot be built in the game. Why not choose some rarely-used air unit as QCUnit (like Gantrithor)? Any side effect of using Gantrithor?
QCUnit cannot be built:
(2)
The 2nd syntax in the MSQC manual doesn't work
( From https://cafe.naver.com/edac/78006 )
The received values are wrong
(3) Nitpick When the [MSQC] section is empty in the edd file, euddraft fails to compile.
[main]
in: ...
out: ...
[MSQC]
[eudTurbo]
(4) Nitpick
MSQC keywords like QCDebug
, QCUnit
are case-sensitive. If user entered the wrong case or mis-spelled the keyword, the error log will be:
File "...\plugins\MSQC.py", line 422, in onInit
increment = int(ret[1], 0)
IndexError: list index out of range
which makes it hard for user to detect the bug.
(1) Why is Valkyrie the default QCUnit? Why not use Gantrithor? Any side effect of using Gantrithor?
Old predecessor EasyNQC
showcased valkyrie as QC unit. https://cafe.naver.com/edac/40357
(2) The 2nd syntax in the MSQC manual doesn't work
Sorry it's already pointed out in comment too but I forgot to fix.
(3) When the [MSQC] section is empty in the edd file, euddraft fails to compile.
[MSQC]
adds non-trivial amount of triggers in your map, so it is intended to raise error when you add [MSQC]
in plugins without any input. The error message (Forward declaration is not initialized) is not helpful though.
(4) MSQC keywords like QCDebug, QCUnit are case-sensitive. If user entered the wrong case or mis-spelled the keyword, the error log will be:
Yeah error message is misleading and ideally it should suggest correction.
Thx. Btw, do u have the most comprehensive memory table? I'm curious about the memory address storing whether the local user is using Remastered UHQ graphics (not necessarily wide screen) or the original graphics. And so on... I only have this one: http://farty1billion.dyndns.org/EUDDB/ But it is incomplete and somehow outdated. Eg. It doesn't have the entries of 0x654880 and 0x654AA0, the QueueGameCommand ones.
For all I know EUD DB is still most exhaustive and complete one, not most informative for individual entries but best overview. For 0x654880 and 0x654AA0 they haven't documented well and people who researched those removed posts and left cafe so IMO eudplib internal is best to see what it does.
Let me bring a conversation about what they do at eud channel in SEN discord.
Armo — 03/11/2021 I know how to make human player ping at point. Added as euddraft function QueueGameCommand_MinimapPing(x + y * 65536) Armo — 03/11/2021 Looks yellow for oneself (not sure for allies) Zoan — 03/11/2021 Oh, that works ayylmao — 03/11/2021 What does the minimap ping command look like in raw writes? Armo — 03/11/2021 Kinda complex to explain... https://github.com/phu54321/eudplib/blob/066c0faa200dc19e70cdb6979daf8f008b8ae957/eudplib/eudlib/qgcf/qgc.py#L43 lol it's missing in eud db xd Game command queue is at 0x654880, its length is at 0x654AA0. https://github.com/phu54321/vgce/blob/0da9d14626c06351e66a3d9c18973f6d69b89a7a/docs/Blizzard/Starcraft/packets2.txt#L41 Packet Structure for minimap ping Probably @𝙃𝙚𝙞𝙣𝙚𝙧M𝙖𝙣𝙣 found these 𝙃𝙚𝙞𝙣𝙚𝙧M𝙖𝙣𝙣 — 03/11/2021 oh vgce is from my repo I need to reupload it to github FaRTy1billion — 03/11/2021 ya, lots of stuff is still missing from euddb and i've been too lazy to add it 𝙃𝙚𝙞𝙣𝙚𝙧M𝙖𝙣𝙣 — 03/11/2021 was surprised people were able to pull it to github I just forked that one back to mine Armo — 03/11/2021 So... for example, size for Minimap ping packet is 5 bytes. 1 bytes for packet kind (0x58) 2 bytes each for x and y (u16) -> Say queue length is currently 42. You need to write 5 bytes starting at 0x654880 + 42 (=0x6548AA~0x6548AE) and add queue length by 5. FaRTy1billion — 03/11/2021 :open_mouth: Armo — 03/11/2021 Sharing local data to sync depends on this too, make player to rclick unit at encoded coordinates and get shared data from its waypoint.
I'm curious about the memory address storing whether the local user is using Remastered UHQ graphics (not necessarily wide screen) or the original graphics.
A long time ago they can be differentiated with font width: https://cafe.naver.com/edac/66971 but not worked currently after strange patch. Now you know whether player run purchased version of SC:R or not, or user language setting with font width https://cafe.naver.com/edac/75968
I'm afraid this is not true.
KeyPress and MouseButton are not tied to trigger execution interval or gametick. MouseDown(R) on frame 2 only shows that you started pushing button between frame 1 and frame 2, and button is still down on frame 2. If you really want to measure it precisely, inspecting game memory is way to go. Btw delay is quite different between solo play and multiplayer.
KeyPress and MouseButton are not tied to trigger execution interval or gametick.
Thx. I also noticed this after I post the comment. So I deleted it and did further tests yesterday. (So github still shows the deleted comment to the Owner???) So I'll just accept this fact.
Other stuffs:
(1) I inspected 0x654880. The select and right click commands are different from what's written here: https://github.com/phu54321/vgce/blob/master/docs/Blizzard/Starcraft/packets2.txt
The right click command has opcode 0x60
and something like:
def QueueGameCommand_RightClick_New(xy):
RightClickCommand = c.Db(b"...\x60XXYY\0\0\0\0\xE4\0\x00")
c.SetVariables(ut.EPD(RightClickCommand + 4), xy)
QueueGameCommand(RightClickCommand + 3, 12)
Though 0x14
also works, I don't know the difference.
The "select" command has opcode 0x63
, and has a unitID
that I cannot decode/parse.
(2) There's typo in qgc.py, https://github.com/armoha/euddraft/issues/27#issuecomment-812032598
(3) A minor request. What's the syntax of edd
file? Can I comment out the unused lines using #
like python? If so, it'll be better if #
also works under [MSQC]
.
(1) Oh 0x60
or 0x63
are not found in https://github.com/bwapi/bwapi/blob/main/bwapi/BWAPI/Source/BW/OrderTypes.cpp too.
Maybe they are added in SC:R, hmm.
alphaID (u16) = 2048 * targetOrderSpecial + index (not scmd one, 0x59CCA8 is 1)
Hotkey, loaded unit, qgc use it for compactness and preventing using different unit of same index.
0xA5 uniquenessIdentifier or targetOrderSpecial increases by 1 when new unit take over that CUnit.
(2) Fixed. Thank you!
(3) There is no comment syntax for .eds/.edd now but it is nice feature request to add!
We don't want to break any [chatEvent]
maps with chat starting with #
so I need to find a better option. 🤔
(2) Fixed. Thank you!
The command opcode also needs to be fixed. It's 0x12
, not 0x2A
https://github.com/armoha/eudplib/blob/master/eudplib/eudlib/qgcf/qgc.py#L153
[header]
key : value pair
key-only
; comment
# comment
.eds/.edd config syntax (supposed) https://github.com/armoha/euddraft/blob/d252474f8553a902e0fe65d81a6d6c265c5b3b57/readconfig.py#L42-L46 This is bug that keyonly has higher priority than comment so has ignored...
So :: comment
would work for all versions of euddraft.
[header]
key : value pair
key-only
:: comment
Got it. kinda like windows batch :D
Closes as completed, please re-open or open new issue if you have any question.
Q1. The VAL syntax of MSQC is:
It requires at least 1 condition. Why is the condition necessary? If I wanna pass the value of a non-shared address to a PVariable, I think I could just intuitively write something like:
But this violates the syntax. So I must add some meaningless condition (which I don't think is necessary), like:
This syntax is a little bit conter-intuitive for me. Could you explain why the syntax requires at least 1 condition?
Q2. What should I do if I want to pass a value which could possibly be very large? Say, my map is 128x128 so that the sendable value is 0 to 4194303. But the value I want to pass is far larger than 4194303.