You are not logged in.

Applications: [GameMaster: OPEN] | [Volunteer Testers: OPEN]


This forum will be permanently shut down on Friday 13.07.2018
Please copy or save all important information from old forum before they will be deactivated
We have moved to new board. https://forum.runesofmagic.gameforge.com/Come join us.

1

Wednesday, November 18th 2015, 12:55am

Replacing arg1 on a chat event.

As a prelude, there was this thread, Fix the translation problems after merger?

So after all this magic went down, I decided to post here,
mainly to attract and bait people like Peryl into helping me out with code.
The main issue I am having, is actually replacing or overwriting locally what appears in the chat window.

Source code

1
2
3
4
5
function Occurrens_Linguae.SubstitueOriginalLink(arg1, originalLinkName, translatedLinkName)
	DEFAULT_CHAT_FRAME:AddMessage(arg1); DEFAULT_CHAT_FRAME:AddMessage(originalLinkName); DEFAULT_CHAT_FRAME:AddMessage(translatedLinkName);
	arg1 = string.gsub(arg1, originalLinkName, translatedLinkName);
	DEFAULT_CHAT_FRAME:AddMessage(arg1);
end

Clarification of output:

Source code

1
2
Default output of line 4-5...:[Saber]
Say(original event): [Not a name]


The problem is arg1 (passed in from Occurrens_Linguae.OnEvent(this, event) ) is returning EXACTLY what its supposed too. But in chat, it wont replace itself. How do I replace something in say chat with my updated arg1? Honestly can't figure it out.

The example I was using was this link... |Hitem:33784|h|cffffffcc[Not a name]|r|h
When it comes back, it should be |Hitem:33784|h|cffffffcc[Saber]|r|h
What is displayed, and should be is [Saber]
I have no idea how too replace arg1. I know its possible because its client side only, and error messages can appear there, and did appear there when i was testing. Is there a method, or a way to make this happen?

This post has been edited 3 times, last edit by "ftwoplay" (Nov 18th 2015, 4:03am)


2

Wednesday, November 18th 2015, 6:12am

In order to fix this, you need to understand how RoM handles it's events. Basically, it works as follows:
  • The RoM Engine sets the global variables arg1, arg2, ... up to its max (i think arg6 or arg7) as far as relevant for the event
  • The RoM Engine calls all handlers registered for this event in arbitrary order (that meaning, you can't be sure on it)


So it might be tempting just to set the global variable arg1 (not the local one of your snippet) to a "translated" version - but this might fail, basically due to whats generally known as a race condition (who gets to handle the event first - your "translator" or the chat?). But why rely on a race condition, when there are more reliable ways to do this? 2 options that come to my mind:
  • hook ChatFrameX:AddMessage (X = 1 to 9)
  • Hook or modify ChatFrame_OnEvent (located int interface/worldxml/chatframe.lua)


Since there are multiple addons trying to modify/catch/... some chat messages, most of the time either one or both methods are already hooked, which might complicate things a bit - so in this case, I would suggest modifying ChatFrame_OnEvent directly.

Good news: I've gone ahead and done it ;) Copy the attached file to "<YourROMFolder>\interface\worldxml\" and remove the ".txt" extension (had to rename it to upload it here). If you want to see how I did it exactly, look at lines 448-455 (just some pattern magic)

EDIT: To clarify, the basic problem in your Occurrens_Linguae.SubstituteOriginalLink is that you're only modifying a local variable named "arg1" (the parameter). Strings in Lua are treated like values, not like references. They are "passed-by-value" (!), so local modifications stay local, unless you assign them to a global (or upvalue) or return the modified version (and ofc the caller then does something with it).

If that was too high of a level or you still have some other questions, feel free to ask ;)

EDIT 2: I hope you don't mind me posting a solution after you put in some effort yourself - I saw your question here, looked at the link you posted, read the problem there, got the idea how to fix it, returned here, saw your problem (and that you've probably taken a wrong path in solving this - listening for chat events on your own won't fix the original chat messages)
hoffmale has attached the following file:

This post has been edited 2 times, last edit by "hoffmale" (Nov 18th 2015, 7:04am)


3

Wednesday, November 18th 2015, 2:40pm

I understand what I was doing wrong, and why. This is a solution, but possibly a valid one. I'll have to hook the event most likely, which is not an ideal solution, but is necessary to make sure the solution is easily propogatable, and not illegal. I appreciate the effort you expended, I really do. But I'm sure you understand why I am hesitant to accept this solution. I'm not sure if having a very small WaitTimer.Wait will help. I am unsure of if the event handler for RoM is multi threaded, if it is. We can force mine to fire last via a wait timer. If not, a race condition will have to do...

Edit: If for some reason Players are allowed to modify or use specific modifications to the in game .lua I would be MORE than happy to use this solution and start fixing like every bug that exists in game. I doubt this is the case, but if a GM would kindly let me know where we stand on the issue. This modification is harmless, and I believe should be allowed... My dreams would come true, if GM's said we can do this. My only desire is to fix the game, and modifying files like these would allow me to go well above and beyond addons. I beg of you GM's. Let this solution be acceptable.

This post has been edited 2 times, last edit by "ftwoplay" (Nov 18th 2015, 3:00pm)


4

Wednesday, November 18th 2015, 3:54pm

I understand what I was doing wrong, and why. This is a solution, but possibly a valid one. I'll have to hook the event most likely, which is not an ideal solution, but is necessary to make sure the solution is easily propogatable, and not illegal. I appreciate the effort you expended, I really do. But I'm sure you understand why I am hesitant to accept this solution. I'm not sure if having a very small WaitTimer.Wait will help. I am unsure of if the event handler for RoM is multi threaded, if it is. We can force mine to fire last via a wait timer. If not, a race condition will have to do...


In all likelyhood, the chat event handler will be triggered first (standard UI is loaded first, so unless there's something weird going on, it should register first for the chat message events (and subsequently most of the time it will then be triggered first). But in order to modify the message before it will be displayed, YOU would have to be first. (Modifying after ChatFrameX:AddMessage returned won't change the displayed text). It's like printing: You have to change the text before you print it in order to get the change on the paper. (As you probably notice, WaitTimer won't help you here - it can't rewind time).

The event handler itself isn't multithreaded, but there's no way (as far as I can tell) to influence the execution order. Most of the time it seems to trigger the handlers in registration order, but there are deviations.

Normally, I'm hesistant to modify (or, more precisely: override) the standard UI files, but in this case it seemed the most reliable way. I could overwrite the function in an addon, but that might be loaded too late (if another addon has already hooked it, then that addon wouldn't work anymore since we've now overwritten their hook. We could hook it ourselves, but that again can cause troubles). You would be surprised how many addons try to do some stuff with the chat messages...

Edit: If for some reason Players are allowed to modify or use specific modifications to the in game .lua I would be MORE than happy to use this solution and start fixing like every bug that exists in game. I doubt this is the case, but if a GM would kindly let me know where we stand on the issue. This modification is harmless, and I believe should be allowed... My dreams would come true, if GM's said we can do this. My only desire is to fix the game, and modifying files like these would allow me to go well above and beyond addons. I beg of you GM's. Let this solution be acceptable.

Well, from what I understand we can customize our interface folder (which means, we can customize the login screen and the standard UI). Sadly, as the name says, there are only interface relevant files in there, so you would only be able to fix interface bugs (like, remove that annoying diamond minimap button ^^). Graphics engine, AI and game logic are mostly hardcoded anyways, so no lua files to modify there either.

5

Wednesday, November 18th 2015, 4:07pm

Well, from what I understand we can customize our interface folder (which means, we can customize the login screen and the standard UI). Sadly, as the name says, there are only interface relevant files in there, so you would only be able to fix interface bugs (like, remove that annoying diamond minimap button ^^). Graphics engine, AI and game logic are mostly hardcoded anyways, so no lua files to modify there either.
That's too bad... I mean I guess customizing the interface is a fun project, but oh well... In any case, Thank you for your help, and I don't mid that you solved the problem. Perhaps we could recommend the code to RoM Dev's (RW) itself. This wouldn't be a huge change, and would be very helpful, to some at least. What I was thinking though, was I could fix things like the stupid EoJ timer disappearing like every 30 seconds :P. Stuff like that. In any case very helpful, and very informative. I am very grateful for your assistance with this matter. I was hoping there would be a way that they COULD pass the arg by reference, or perhaps a method callback, or even a setter. But alas no such luck. And with that being said. If your above post is true, that we can edit the interface files, this solution is perfectly valid, and everyone who wishes, should follow the instruction in your first post here.

With complete gratitude,
Stoneforge [Mithras, Erebos]

6

Wednesday, November 18th 2015, 4:27pm

fix things like the stupid EoJ timer disappearing like every 30 seconds

There's no real fix for that, just a workaround (you can run a timer on you own after you've detected that the event runs, but you won't be able to display the progress).

I was hoping there would be a way that they COULD pass the arg by reference, or perhaps a method callback, or even a setter.
Well, theoretically you could, but that would require some modifications anyways ;) (Well, also you could unregister the original handler and call ChatFrameX:AddMessage or the original handler from your handler - wihich might or might not break other addons).

If your above post is true, that we can edit the interface files, this solution is perfectly valid, and everyone who wishes, should follow the instruction in your first post here.
Well, even if it would wouldn't be alowed it would be easy to do in another addon ("ChatFrame_OnEvent = insert_modified_version_here"), but with some possible conflicts with other addons (as I said, that function is contented ^^)

7

Wednesday, November 18th 2015, 4:53pm

As for the EoJ timer, which is slightly off-topic (the original topic is resolved.) I'm not sure if you have ever bothered to look at my addon SMPA, but I did exactly that, although the math is finicky because of switching channels (WaitTimer.Wait and most code in general doesn't fire while swapping channels... So I had to guestimate...) [Also note, my code is very messy, and probably unnecessary. :P]. In addition, getting the timer is dependent on another player being there, when the event started, that also has the addon. This is the best I could do at the time, and it sounds like you agree, this is the best it is going to get, because the only way to improve it, would be adding the score, which would result in me having to update every other client with the score of each client... Which is incredibly wasteful, and unnecessary. Not to mention the fact that some people may not have the addon, which then again throws the score out of whack.



In case you want to gander through it. Not forcing you to...

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
--This array stores information about the Public_Event that is happening in the zone and the SMPA_PublicEventFrame. 
local SMPA_PE = { 
CHAN1 = false; --Has the event finished on Channel 1? 
CHAN2 = false; --Has the event finished on Channel 1? 
CHAN3 = false; --Has the event finished on Channel 1? 
CHANNELCHANGED = false; --Have we just changed a channel, THAT we haven't been to yet this event? 
PREVIOUSCHANNEL; --What channel were we on, before our current one? (Code doesn't execute for the 25 seconds as you are switching channels) 
CHANGEOCCUR = false; --Have we just changed a channel? 
CHANGELINGER = false; --I don't even know, and I coded this recently. If you figure it let me know. 
SUCCESS = false; --Did the event succeed? 
TIMER = -1; --What is the event's timer? 
WAITING = false; --Are we waiting (the time in-between function calls.) 
MY_REQUEST = false; --Did I ask for the "!EOJ" update? 
RUNNING = false; --Are we running the Public event code? 
VALID = true; --Are we OK to run the loop? You don't want it running during a load screen etc... 
} 
---------------------------------------------------- 
-------------- Public Event Code -------------- 
---------------------------------------------------- 
--Determines whether the event is: running, waiting, or just ended. 
function SMPAPublicEventPhase(arg1, timer) 
if (SMPAFindName(arg1, SMPA_MSG.PUBLIC_EVENT_STOP)) then 
SMPA_PE.RUNNING = false; 
SMPA_PE.VALID = true; 
SMPA_PE.SUCCESS = false; 
current = GetCurrentParallelID(); 
SMPAPublicEventChannel(current); 
SMPA_PublicEventFramePhase:SetText("Waiting"); 
SMPA_PublicEventFramePhase:SetColor(1, 0.5, 0); 
if (timer == nil) then 
timer = 124; 
end 
WaitTimer.Wait(2, SMPAPublicEventWaitTimer, tmrPESuccess, timer); 
--SMPAColorAttentionPlease("Typing '!EOJ' will update your timer.", 1, 1, 0); 
elseif (SMPAFindName(arg1, SMPA_MSG.PUBLIC_EVENT_START)) then 
SMPA_PE.RUNNING = true; 
SMPA_PE.VALID = true; 
SMPAPublicEventChannelReset(); 
SMPA_PublicEventFramePhase:SetText("Running"); 
SMPA_PublicEventFramePhase:SetColor(0.1, 1, 0); 
if (timer == nil) then 
timer = 1100; 
end 
SMPAPublicEventTimer(timer); 
elseif (SMPAFindName(arg1, "The 3 adventurers who made the highest")) then 
SMPA_PE.SUCCESS = true; 
end 
end 
--The timer function for a RUNNING public event. 
function SMPAPublicEventTimer(passedTime) 
if (SMPA_PE.VALID) then 
local newTime = passedTime - 1; 
SMPA_PE.TIMER = passedTime; 
if (SMPA_PublicEventFramePhase:GetText() == "Running") then 
if (passedTime == 1078) then 
SMPA_PublicEventFrameCountdown:SetColor(0, 1, 0); 
elseif (passedTime == 600) then 
SMPA_PublicEventFrameCountdown:SetColor(0.2, 0.8, 0); 
elseif (passedTime == 300) then 
SMPA_PublicEventFrameCountdown:SetColor(0.5, 0.5, 0); 
elseif (passedTime == 180) then 
SMPA_PublicEventFrameCountdown:SetColor(0.8, 0.2, 0); 
elseif (passedTime == 60) then 
SMPA_PublicEventFrameCountdown:SetColor(1, 0, 0); 
end 
end 

local current = GetCurrentParallelID(); 
if (current == SMPA_PE.PREVIOUSCHANNEL) then 
-- 
else 
if (current == 1 and SMPA_PE.CHAN1 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
elseif (current == 2 and SMPA_PE.CHAN2 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
elseif (current == 3 and SMPA_PE.CHAN3 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
end 
SMPA_PE.CHANGEOCCUR = true; 
SMPA_PE.CHANGELINGER = true; 
end 

if (SMPA_PE.RUNNING) then 
if (passedTime == 0) then 
--We must be done counting down now. 
SMPA_PublicEventFramePhase:SetText("Resetting..."); 
--SMPA_PublicEventFrameCountdown:SetText("Resetting..."); 
else 
local convertedTime = SMPAConvertToMinutes(newTime); 
SMPA_PublicEventFrameCountdown:SetText(convertedTime); 
if (SMPA_PE.CHANGEOCCUR) then 
SMPA_PE.CHANGEOCCUR = false; 
local newPassTime = newTime - 25; 
if (SMPA_PE.CHANNELCHANGED) then 
WaitTimer.Wait(1, SMPAPublicEventTimer, tmrPublicEvent, newPassTime); 
else 
SMPA_PE.CHANNELCHANGED = false; 
newPassTime = newPassTime + 120; 
SMPA_PublicEventFramePhase:SetText("Waiting"); 
SMPA_PublicEventFramePhase:SetColor(1, 0.5, 0); 
SMPA_PE.RUNNING = false; 
WaitTimer.Wait(1, SMPAPublicEventWaitTimer, tmrPublicEvent, newPassTime); 
end 
else 
WaitTimer.Wait(1, SMPAPublicEventTimer, tmrPublicEvent, newTime); 
end 
end 
end 
end 
end 
--The timer function for a WAITING public event. 
function SMPAPublicEventWaitTimer(passedTime) 
if (SMPA_PE.SUCCESS) then 
passedTime = passedTime + SMPA_PE.TIMER; 
SMPA_PE.SUCCESS = false; 
end 
if (SMPA_PE.VALID) then 
local newTime = passedTime - 1; 
SMPA_PE.TIMER = passedTime; 
if (SMPA_PublicEventFramePhase:GetText() == "Waiting") then 
if (passedTime == 600) then 
SMPA_PublicEventFrameCountdown:SetColor(1, 0, 0); 
elseif (passedTime == 300) then 
SMPA_PublicEventFrameCountdown:SetColor(0.8, 0.2, 0); 
elseif (passedTime == 120) then 
SMPA_PublicEventFrameCountdown:SetColor(0.5, 0.5, 0); 
elseif (passedTime == 60) then 
SMPA_PublicEventFrameCountdown:SetColor(0.2, 0.8, 0); 
elseif (passedTime == 20) then 
SMPA_PublicEventFrameCountdown:SetColor(0, 1, 0); 
end 
end 

local current = GetCurrentParallelID(); 
if (current == SMPA_PE.PREVIOUSCHANNEL) then 
-- 
else 
if (current == 1 and SMPA_PE.CHAN1 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
elseif (current == 2 and SMPA_PE.CHAN2 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
elseif (current == 3 and SMPA_PE.CHAN3 == false) then 
SMPA_PE.CHANNELCHANGED = true; 
SMPA_PE.PREVIOUSCHANNEL = current; 
end 
SMPA_PE.CHANGEOCCUR = true; 
SMPA_PE.CHANGELINGER = true; 
end 


if (SMPA_PE.RUNNING == false) then 
if (passedTime == 0) then 
--We must be done counting down now. 
SMPA_PublicEventFramePhase:SetText("Resetting..."); 
--SMPA_PublicEventFrameCountdown:SetText("Resetting..."); 
else 
local convertedTime = SMPAConvertToMinutes(newTime); 
SMPA_PublicEventFrameCountdown:SetText(convertedTime); 
if (SMPA_PE.CHANGEOCCUR) then 
newTime = newTime - 25; 
SMPA_PE.CHANGEOCCUR = false; 
if (SMPA_PE.CHANNELCHANGED) then 
SMPA_PE.CHANNELCHANGED = false; 
local newPassTime = newTime - 120; 
SMPA_PublicEventFramePhase:SetText("Running"); 
SMPA_PublicEventFramePhase:SetColor(0.1, 1, 0); 
SMPA_PE.RUNNING = true; 
WaitTimer.Wait(1, SMPAPublicEventTimer, tmrPublicEvent, newPassTime); 
else 
WaitTimer.Wait(1, SMPAPublicEventWaitTimer, tmrPublicEvent, newTime); 
end 
else 
WaitTimer.Wait(1, SMPAPublicEventWaitTimer, tmrPublicEvent, newTime); 
end 
end 
end 
end 
end 
-- Forcibly updates your timer. 
-- Format: SMPA: phase time 
function SMPAPublicEventUpdater(arg1) 
--Check to see if this a unique version of this function. 
if (SMPA_PE.WAITING == false and SMPA_PE.MY_REQUEST == false) then 
SMPA_PE.WAITING = true; 
local split = SMPAStringSplit(arg1, "%s"); 
local phase = split[2]; 
local timer = split[3]; 
if (phase == "Running") then 
SMPA_PE.RUNNING = true; 
SMPAPublicEventPhase(SMPA_MSG.PUBLIC_EVENT_START, timer); 
elseif (phase == "Waiting") then 
SMPA_PE.RUNNING = false; 
SMPAPublicEventPhase(SMPA_MSG.PUBLIC_EVENT_STOP, timer); 
end 

WaitTimer.Wait(0.2, SMPAResetPublicEventUnique, tmrPublicEventUnique) 
end 
end 
--Responds to a call in zone chat for a timer update. 
function SMPAPublicEventPropogate() 
local phase = SMPA_PublicEventFramePhase:GetText(); 
if (phase == "Waiting" or phase == "Running") then 
SMPAZoneOut("SMPA: "..phase.. " "..SMPA_PE.TIMER); 
end 
end 
--Asks in zone chat for a timer update. 
function SMPAPublicEventSendRequest() 
SMPA_PE.MY_REQUEST = true; 
SMPAZoneOut("!EOJ"); 
WaitTimer.Wait(2, SMPAResetPublicEventRequest, tmrPublicEventRequest); 
end 
--Figures out which channel we are, that the public event just finished. 
function SMPAPublicEventChannel(number) 
if (number == 1) then 
SMPA_PE.CHAN1 = true; 
elseif (number == 2) then 
SMPA_PE.CHAN2 = true; 
elseif (number == 3) then 
SMPA_PE.CHAN3 = true; 
end 
end 
--Resets all the channels, after an event has started again. 
function SMPAPublicEventChannelReset() 
SMPA_PE.CHAN1 = false; 
SMPA_PE.CHAN2 = false; 
SMPA_PE.CHAN3 = false; 
end 
--Stops any loops for public event. 
function SMPAPEStop() 
SMPA_PE.VALID = false; 
end 

function SMPAResetPublicEventUnique() 
SMPA_PE.WAITING = false; 
end 
--Resets whether or not you sent the !EoJ update for the Public_Event. 
function SMPAResetPublicEventRequest() 
SMPA_PE.MY_REQUEST = false; 
end




On top of what's listed, I'm checking for the system messages. And I'm checking whenever we zone change, and when we do, is it a real zone change, or just a channel change... Like I said very messy (I could probably combine the 2 wait timers...), but it works, as well as it probably ever will.

8

Wednesday, November 18th 2015, 5:10pm

Well, you said that one doesn't get timer updates during channel changes. That is not entirely true! Only child frames of UIParent don't get timer updates, since UIParent is hidden during the channel change (however, child frames of WorldFrame DO get timer updates if they are "shown"!).

So you could either change the parent of the WaitTimer frame to WorldFrame (note: change at creation, :SetParent is buggy) or use your own timer frame (which is a direct child of WorldFrame). This would only be thwarted by loading screens, but then again, you normally won't port somewhere else and instantly start doing the EoJ event there in the current round ;)

Other than that... maybe use something like pastebin for larger code samples, it's much much easier to read ;)

9

Wednesday, November 18th 2015, 5:25pm

Yeah, I'm not entirely comfortable moving the frame's parent to world. As for the formatting, it looked fine in preview, and then it decided to remove all of the tabbing... My code already stops itself form running at loading screens, I believe so anyway. I had an issue with this in the past, and I figure that out on the ZONE_CHANGE event or whatever its name is. At this point, going back and editing that code, and some other fragments is probably time better spent elsewhere. For example, I've had some requests recently to help out with some instance notifications...

After all that mess of code, the only thing it really does, is keep track of the progress through channel swapping's. And if someone ever was to type !EoJ or press the refresh button it would update your timer with the correct info (assuming your timer wasn't already running.) The funny times are on line 31 (did you know the actual reset time is 124 seconds? I didn't :P)

The other funny calcs are on line 96 and 165. Where I directly minus out 25, but again this is a estimation, and could be off by up to a second depending on the channel swapping lag. Not a big deal one way or the other. I just wish that the in-game frames could be more dependent or modifiable, rather than stubborn and hardcoded. :P Makes it so difficult to do things manually. Not that I mind the challenge, but I feel like they are setting up obstacles unnecessarily sometimes. :dash:

Now that I think about it, the fact that the PEFs is a descendant of UIParent, is actually the issue on why it wont save when keeping channels. But it still eludes me as to why it won't come back when you swap zone (sub-zone)'s. It does that in channel 1. And I know its not a channel issue. Unless they are using GetCurrentWorldMapID() instead of GetZoneID() for some silly reason...

10

Wednesday, November 18th 2015, 5:41pm

Short answer: the core logic of those event timer displays is hardcoded. The interface generally works, just some of the required calls from the engine don't happen - probably some bug in the networking code (I'm guessing ofc, but since each channel per zone seems to be some kind of own server instance it might not be far off ^^)