asingingbird Posted June 13, 2021 Share Posted June 13, 2021 I am working on a mod called event clock, basically, it's a gui version of "advanced monster warning", so players could check monster timer with a hotkey. But the code I wrote seems to have some bugs in it, I had debuged the mod for the whole day, still no clue how to fix it. Really hope someone so kind could give me some hint, thanks a lot !!!! I post my code here, you can also download the whole project in the attachment. And the last part is some logs the mod outputs and my analysis. Here is what I have found. The method `EventClockScreen:Open()` should run on the server, but it runs on the client side instead, which makes `TheWorld.components.hounded` a nil value, so I can't get attack time of hound, as shown in the screenshot above. Any kind of help is appreciated !!! ```modmain.lua GLOBAL.setmetatable(env,{__index=function(t,k) return GLOBAL.rawget(GLOBAL,k) end}) ENABLE_GUI_TIMER = GetModConfigData("enable_gui_timer") ENABLE_INCOMING_WARNING = GetModConfigData("enable_incoming_warning") LANGUAGE = GetModConfigData("language") Assets = { Asset("ATLAS", "images/hound.xml"), Asset("ATLAS", "images/worm.xml"), Asset("ATLAS", "images/bearger.xml"), Asset("ATLAS", "images/deerclops.xml"), Asset("ATLAS", "images/sinkhole.xml"), } EventClockScreen = GLOBAL.require("screens/eventclock") print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%", TheWorld) AddPrefabPostInit("world", function(inst) print("$$$$$$$$$$$$$$$$$$$$$$$$$$$", TheWorld, TheWorld.ismastersim, TheWorld.components, TheWorld.components.hounded) function displayEventClockScreen() if EventClockScreen:Open() then TheFrontEnd:PushScreen(EventClockScreen) end end function closeEventClockScreen() EventClockScreen:Close() end if ENABLE_GUI_TIMER then --open gui timer window with hotkey `C` GLOBAL.TheInput:AddKeyDownHandler(KEY_C, displayEventClockScreen) GLOBAL.TheInput:AddKeyUpHandler(KEY_C, closeEventClockScreen) end end) ``` ```scripts/screens/eventclock.lua local Screen = require "widgets/screen" local Widget = require "widgets/widget" local Image = require "widgets/image" local Text = require "widgets/text" local TEMPLATES = require "widgets/redux/templates" TIMER_UPDATE_SEG = 4 LANGUAGE = "english" local function localization() if LANGUAGE == "english" then return { DAYS = " days", HOUND = "hound attacks in ", WORM = "worm attacks in ", BEARGER = "bearger attacks in ", DEERCLOPS = "deerclops attacks in ", SINKHOLE = "sinkhole falls in " } end end WARN_MESSAGE = localization() local function second_to_day(sec) if sec then local time_in_days = string.format("%.2f", sec / TUNING.TOTAL_DAY_TIME) return time_in_days..WARN_MESSAGE.DAYS end return "- - -" end EventClockScreen = Class(Screen, function(self) if self.updateTask ~= nil then print("######################update_timer", self.UpdateTimer) --update monster timer periodically self.updateTask = ThePlayer:DoPeriodicTask(TIMER_UPDATE_FREQUENCY, self.UpdateTimer) end end) function EventClockScreen:HoundTimeLeft() local time_left print("########## in HoundTimeLeft", TheWorld, TheWorld.ismastersim, TheWorld.components.hounded) if TheWorld.ismastersim and TheWorld.components.hounded then time_left = TheWorld.components.hounded:GetTimeToAttack() end print("######################HoundTimeLeft", time_left) return time_left end local DEERCLOPS_TIMERNAME = "deerclops_timetoattack" function EventClockScreen:DeerclopsAttackTimeLeft() local time_left print("########## in DeerclopsAttackTimeLeft", TheWorld, TheWorld.ismastersim, TheWorld.components.deerclopsspawner, TUNING.DEERCLOPS_ATTACKS_OFF_SEASON or TheWorld.state.iswinter) if TheWorld.ismastersim and TheWorld.components.deerclopsspawner and (TUNING.DEERCLOPS_ATTACKS_OFF_SEASON or TheWorld.state.iswinter) then time_left = TheWorld.components.worldsettingstimer:GetTimeLeft(DEERCLOPS_TIMERNAME) end print("######################DeerclopsAttackTimeLeft", time_left) return time_left end local BEARGER_TIMERNAME = "bearger_timetospawn" function EventClockScreen:BeargerSpawnTimeLeft() local time_left print("########## in BeargerSpawnTimeLeft", TheWorld, TheWorld.ismastersim, TheWorld.components.beargerspawner, TheWorld.state.isautumn) if TheWorld.ismastersim and TheWorld.components.beargerspawner and TheWorld.state.isautumn then time_left = TheWorld.components.worldsettingstimer:GetTimeLeft(BEARGER_TIMERNAME) end print("######################BeargerSpawnTimeLeft", time_left) return time_left end local ANTLION_RAGE_TIMER = "rage" function EventClockScreen:SinkHoleTimeLeft() local time_left print("########## in SinkHoleTimeLeft", TheWorld, TheWorld.ismastersim, TheWorld.components.sinkholespawner, TheWorld.state.issummer) if TheWorld.ismastersim and TheWorld.components.sinkholespawner and TheWorld.state.issummer then time_left = TheWorld.components.worldsettingstimer:GetTimeLeft(ANTLION_RAGE_TIMER) end print("######################SinkHoleTimeLeft", time_left) return time_left end function EventClockScreen:Open() if self.active then return end Screen._ctor(self, "EventClockScreen") self.active = true --darken everything behind the dialog if self.black then self.black:Kill() end self.black = self:AddChild(Image("images/global.xml", "square.tex")) self.black:SetVRegPoint(ANCHOR_MIDDLE) self.black:SetHRegPoint(ANCHOR_MIDDLE) self.black:SetVAnchor(ANCHOR_MIDDLE) self.black:SetHAnchor(ANCHOR_MIDDLE) self.black:SetScaleMode(SCALEMODE_FILLSCREEN) self.black:SetTint(0,0,0,0) if self.proot then self.proot:Kill() end self.proot = self:AddChild(Widget("ROOT")) self.proot:SetVAnchor(ANCHOR_MIDDLE) self.proot:SetHAnchor(ANCHOR_MIDDLE) self.proot:SetPosition(0,0,0) self.proot:SetScaleMode(SCALEMODE_PROPORTIONAL) --throw up the background if self.bg then self.bg:Kill() end self.bg = self.proot:AddChild(TEMPLATES.RectangleWindow(200, 360, "Event Clock")) --add boss icon if TheWorld:HasTag("cave") then --worm self.worm_icon = self.proot:AddChild(Image("images/worm.xml", "worm.tex")) self.worm_icon:SetScale(0.15) self.worm_icon:SetPosition(-40, 70) self.worm_timer = self.proot:AddChild(Text(NEWFONT_SMALL, 16)) self.worm_timer:SetPosition(40, 70) self.worm_timer:SetString(second_to_day(self:HoundTimeLeft())) else --hound self.hound_icon = self.proot:AddChild(Image("images/hound.xml", "hound.tex")) self.hound_icon:SetScale(0.15) self.hound_icon:SetPosition(-40, 70) self.hound_timer = self.proot:AddChild(Text(NEWFONT_SMALL, 16)) self.hound_timer:SetPosition(40, 70) self.hound_timer:SetString(second_to_day(self:HoundTimeLeft())) end --bearger self.bearger_icon = self.proot:AddChild(Image("images/bearger.xml", "bearger.tex")) self.bearger_icon:SetScale(0.15) self.bearger_icon:SetPosition(-40, 10) self.bearger_timer = self.proot:AddChild(Text(NEWFONT_SMALL, 16)) self.bearger_timer:SetPosition(40, 10) self.bearger_timer:SetString(second_to_day(self:BeargerSpawnTimeLeft())) --deerclops self.deerclops_icon = self.proot:AddChild(Image("images/deerclops.xml", "deerclops.tex")) self.deerclops_icon:SetScale(0.15) self.deerclops_icon:SetPosition(-40, -50) self.deerclops_timer = self.proot:AddChild(Text(NEWFONT_SMALL, 16)) self.deerclops_timer:SetPosition(40, -50) self.deerclops_timer:SetString(second_to_day(self:DeerclopsAttackTimeLeft())) --sinkhole self.sinkhole_icon = self.proot:AddChild(Image("images/sinkhole.xml", "sinkhole.tex")) self.sinkhole_icon:SetScale(0.15) self.sinkhole_icon:SetPosition(-40, -110) self.sinkhole_timer = self.proot:AddChild(Text(NEWFONT_SMALL, 16)) self.sinkhole_timer:SetPosition(40, -110) self.sinkhole_timer:SetString(second_to_day(self:SinkHoleTimeLeft())) return self.active end function EventClockScreen:Close() if self.active then self.active = false TheFrontEnd:PopScreen() end end function EventClockScreen:AnnounceIncoming(time_left, action) --announce monster attacks one day ahead if ENABLE_INCOMING_WARNING and time_left and time_left >= TUNING.TOTAL_DAY_TIME and time_left < TUNING.TOTAL_DAY_TIME + TIMER_UPDATE_FREQUENCY then local message = action..second_to_day(time_left)..WARN_MESSAGE.DAYS TheNet:Say(STRINGS.LMB .. " " .. message, TheInput:IsKeyDown(KEY_CTRL)) end end function EventClockScreen:UpdateTimer() --update monster timer local hound_time_left = self:HoundTimeLeft() local bearger_spawn_time_left = self:BeargerSpawnTimeLeft() local deerclops_attack_time_left = self:DeerclopsAttackTimeLeft() local sinkhole_time_left = self:SinkHoleTimeLeft() if TheWorld:HasTag("cave") then self:AnnounceIncoming(hound_time_left, WARN_MESSAGE.WORM) else self:AnnounceIncoming(hound_time_left, WARN_MESSAGE.HOUND) end self:AnnounceIncoming(bearger_spawn_time_left, WARN_MESSAGE.BEARGER) self:AnnounceIncoming(deerclops_attack_time_left, WARN_MESSAGE.DEERCLOPS) self:AnnounceIncoming(sinkhole_time_left, WARN_MESSAGE.SINKHOLE) if self.active then if TheWorld:HasTag("cave") then self.worm_timer:SetString(second_to_day(hound_time_left)) else self.hound_timer:SetString(second_to_day(hound_time_left)) end self.bearger_timer:SetString(second_to_day(bearger_spawn_time_left)) self.deerclops_timer:SetString(second_to_day(deerclops_attack_time_left)) self.sinkhole_timer:SetString(second_to_day(sinkhole_time_left)) end end print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") print(TheWorld) print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") return EventClockScreen ``` I added some debug log, on the server, I got logs like this, which means `TheWorld.components.hounded` does exist on server : ``` [00:00:08]: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ [00:00:08]: nil [00:00:08]: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ [00:00:08]: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% nil [00:00:15]: $$$$$$$$$$$$$$$$$$$$$$$$$$$ 100026 - world true table: 0EB43518 table: 1376FCF0 ``` but on client, I got these logs, meaning `displayEventClockScreen` method runs on client side, that's why it's not working : ``` [00:01:11]: ########## in HoundTimeLeft 100028 - world false nil [00:01:11]: ######################HoundTimeLeft nil [00:01:11]: ########## in BeargerSpawnTimeLeft 100028 - world false nil true [00:01:11]: ######################BeargerSpawnTimeLeft nil [00:01:11]: ########## in DeerclopsAttackTimeLeft 100028 - world false nil false [00:01:11]: ######################DeerclopsAttackTimeLeft nil [00:01:11]: ########## in SinkHoleTimeLeft 100028 - world false nil false [00:01:11]: ######################SinkHoleTimeLeft nil [00:01:16]: ########## in HoundTimeLeft 100028 - world false nil [00:01:16]: ######################HoundTimeLeft nil [00:01:16]: ########## in BeargerSpawnTimeLeft 100028 - world false nil true [00:01:16]: ######################BeargerSpawnTimeLeft nil [00:01:16]: ########## in DeerclopsAttackTimeLeft 100028 - world false nil false [00:01:16]: ######################DeerclopsAttackTimeLeft nil [00:01:16]: ########## in SinkHoleTimeLeft 100028 - world false nil false [00:01:16]: ######################SinkHoleTimeLeft nil [00:01:25]: ########## in HoundTimeLeft 100028 - world false nil [00:01:25]: ######################HoundTimeLeft nil [00:01:25]: ########## in BeargerSpawnTimeLeft 100028 - world false nil true [00:01:25]: ######################BeargerSpawnTimeLeft nil [00:01:25]: ########## in DeerclopsAttackTimeLeft 100028 - world false nil false [00:01:25]: ######################DeerclopsAttackTimeLeft nil [00:01:25]: ########## in SinkHoleTimeLeft 100028 - world false nil false [00:01:25]: ######################SinkHoleTimeLeft nil [00:01:27]: ########## in HoundTimeLeft 100028 - world false nil [00:01:27]: ######################HoundTimeLeft nil [00:01:27]: ########## in BeargerSpawnTimeLeft 100028 - world false nil true [00:01:27]: ######################BeargerSpawnTimeLeft nil [00:01:27]: ########## in DeerclopsAttackTimeLeft 100028 - world false nil false [00:01:27]: ######################DeerclopsAttackTimeLeft nil [00:01:27]: ########## in SinkHoleTimeLeft 100028 - world false nil false [00:01:27]: ######################SinkHoleTimeLeft nil ``` my_mod.zip Link to comment Share on other sites More sharing options...
Monti18 Posted June 14, 2021 Share Posted June 14, 2021 You need to use GLOBAL.TheWorld in the modmain, otherwise the world is always nil. Screens are always running on the client, you will need to add netvars to send information from the server to the client, which you can then use with your screen. 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now