Thomas_Nightfire Posted February 6, 2021 Share Posted February 6, 2021 Hello everyone! Usually when I need to sync peds for every player, I have to call triggerClientEvent(getRootElement(), 'someEvent'), but is it right? I've read that every entity (ped, player, vehicle) have an a syncer which is syncing data and tells server about it -> server tells other clients... and I've tried to call triggerClientEvent(getElementSyncer(ped), 'someEvent'), but when I connect other client, only syncer sees ped control state (forwards, in my case), but second player sees ped only changing his pos and Z-rot and no control state changing.. So the question is: I have to trigger event for every client, or the other way exists? Link to comment
Moderators IIYAMA Posted February 6, 2021 Moderators Share Posted February 6, 2021 (edited) 45 minutes ago, Thomas_Nightfire said: but is it right? I've read that every entity (ped, player, vehicle) have an a syncer which is syncing data and tells server about it -> server tells other clients... The syncer (which can be anybody as long as the ped is streamed in for that player) will synchronized the following: Position Rotation Velocity < probably ??? unknown stuff, I did not test everything ??? The most other things are not synchronized. For example: Control state (triggerClientSide/elementData required) Model (unless set by serverside) Dimension/interior (unless set by serverside) See next page 45 minutes ago, Thomas_Nightfire said: So the question is: I have to trigger event for every client, or the other way exists? You have to keep track of the ped control states on serverside. You can use tables for that or even elementData, just pick what you can work with. But you probably want to start with keeping track of loaded players. The following code is used in the beta resource that I shared not long ago. local players = getLoadedPlayers() -- table {player, player, player} triggerClientEvent (players, "event-name", ped, "walk", true) --------------------- local loadedStatus = isPlayerLoaded (player) -- true/false Client Spoiler clientResourceStart = function () triggerServerEvent("onClientPlayerResourceStart", resourceRoot) end addEventHandler("onClientResourceStart", resourceRoot, clientResourceStart, false) Server Spoiler local loadedPlayers = dataManagerConstructor:new() addEvent("onClientPlayerResourceStart", true) addEventHandler("onClientPlayerResourceStart", resourceRoot, function () if isElement(client) then loadedPlayers:setData(client, getTickCount()) end end, false) addEventHandler("onPlayerQuit", root, function () loadedPlayers:removeData(source) end) function getLoadedPlayers() return loadedPlayers:getKeys() end function isPlayerLoaded (player) return loadedPlayers:getData(player) and true or false end My dataManagement Library used in the script above, might be a little bit complex but feel free to use: Spoiler dataManagerConstructor = { new = function (self) local dataManager = { collection = {}, -- Optimised for fast loops registery = {} -- Optimised to speed up finding the right item } local methods = self.methods -- The following can also be done with just a meta table, but that would make the methods slower. Making an instance is just something you do once. for i=1, #methods do local method = methods[i] dataManager[method.key] = method.func end return dataManager end, methods = { { key = "clone", func = function (self) local newCollection = {} local newRegistery = {} local dataManager = { collection = newCollection, registery = newRegistery } local collection = self.collection for i=1, #collection do local item = collection[i] local key = item.key local newItem = { key = key, value = item.value } newCollection[i] = newItem newRegistery[key] = newItem end local methods = dataManagerConstructor.methods for i=1, #methods do local method = methods[i] dataManager[method.key] = method.func end return dataManager end }, { key = "setData", func = function (self, key, value) local registery = self.registery if key ~= nil and not registery[key] then local item = { key = key, value = value } registery[key] = item local collection = self.collection collection[#collection + 1] = item return self, true end return self, false end }, { key = "getData", func = function (self, key) if key ~= nil then local item = self.registery[key] if item then return item.value end end return false end }, { key = "clear", func = function (self) self.collection = {} self.registery = {} return self end }, { key = "getNext", func = function (self, index) local collection = self.collection if index == 0 then index = nil end local foundIndex, item = next(collection, index) if item then return item.key, item.value, foundIndex end end }, { key = "forEach", func = function (self, callBackFunc, inverted) local collection = self.collection if not inverted then for i=1, #collection do local item = collection[i] callBackFunc(item.key, item.value) end else for i=#collection, 1, -1 do local item = collection[i] callBackFunc(item.key, item.value) end end return true end }, { key = "getKeys", func = function (self) local keys = {} local collection = self.collection for i=1, #collection do keys[i] = collection[i].key end return keys end }, { key = "sort", func = function (self, func) local collection = self.collection table.sort(collection, func) return true end }, { key = "getAllData", func = function (self, reduceData) local collection = self.collection -- Format: {key = ..., value = ...} local newCollection = {} for i=1, #collection do local item = collection[i] if reduceData and type(reduceData) == "function" then newCollection[#newCollection + 1] = {key = item.key, value = reduceData(item.value)} else newCollection[#newCollection + 1] = {key = item.key, value = item.value} end end return newCollection end }, { key = "addAllData", func = function (self, collection) for i=1, #collection do local item = collection[i] self:setData(item.key, item.value) end return self, true end }, { key = "removeData", func = function (self, key) local registery = self.registery if key ~= nil then local item = registery[key] if item then registery[key] = nil local collection = self.collection for i=1, #collection do if collection[i] == item then table.remove(collection, i) break end end return self, true end end return self, false end }, } } Edited February 6, 2021 by IIYAMA 1 Link to comment
Thomas_Nightfire Posted February 7, 2021 Author Share Posted February 7, 2021 Quote You have to keep track of the ped control states on serverside You mean set data to ped server-side, and with timer / other often repeating event check this data and do actions on client? Quote But you probably want to start with keeping track of loaded players Loaded players? A variable root isn't include all players currently online? There aren't the same? I'm sorry, I think I do not understand MTA Tree System... Link to comment
Moderators IIYAMA Posted February 7, 2021 Moderators Share Posted February 7, 2021 (edited) 1 hour ago, Thomas_Nightfire said: You mean set data to ped server-side, and with timer / other often repeating event check this data and do actions on client? No, when for example a control state has changed, you keep that information on the server. With this information you can inform new/not loaded clients about the current states of the peds. pedDataCollection = {} -- set-up pedDataCollection[ped] = { running = false } local pedData = pedDataCollection[ped] pedData.running = true It can also reduce data tranfers between the client and the server. So if you know that you set the ped to run, you do not have to tell all clients later again that the ped is running. 1 hour ago, Thomas_Nightfire said: Loaded players? A variable root isn't include all players currently online? There aren't the same? I'm sorry, I think I do not understand MTA Tree System... It is, but it gives error when a client has not loaded their resources. Which is in my opinion not very clean. You want to reduce errors to the minimal to prevent lag. Edited February 7, 2021 by IIYAMA 1 Link to comment
Tekken Posted February 7, 2021 Share Posted February 7, 2021 7 hours ago, IIYAMA said: It is, but it gives error when a client has not loaded their resources. Which is in my opinion not very clean. You want to reduce errors to the minimal to prevent lag. He can have a variable client sided like: local isLoggedIn = false; if isLoggedIn then ...bla bla bla end and only manage peds for players that have this variable true and when login set the variable true, this should work, right? 1 Link to comment
Moderators IIYAMA Posted February 7, 2021 Moderators Share Posted February 7, 2021 4 hours ago, Tekken said: and only manage peds for players that have this variable true and when login set the variable true, this should work, right? Yes that could work in terms of ped management. But that does not solve the hard coded error, that will occur when the client has not added the <event> yet, while receiving <that event> from serverside. It can even lead to desync when serverside is not sure if the client has received it's message. Sure you can repeatedly send the same data, but that will be an unnecessary data transfers (in my opinion). You want to start the dialogue between clientside and serverside when you know that both sides are ready, so that your resource can be optimized for data reduction. 1 Link to comment
Thomas_Nightfire Posted February 9, 2021 Author Share Posted February 9, 2021 (edited) On 07/02/2021 at 16:42, IIYAMA said: Yes that could work in terms of ped management. But that does not solve the hard coded error, that will occur when the client has not added the <event> yet, while receiving <that event> from serverside. It can even lead to desync when serverside is not sure if the client has received it's message. Sure you can repeatedly send the same data, but that will be an unnecessary data transfers (in my opinion). You want to start the dialogue between clientside and serverside when you know that both sides are ready, so that your resource can be optimized for data reduction. Thanks a lot, undertood. But to not make another topic, how can I update peds frequently? I mean serverside timer with ~100ms will be bad, like u said earlier. I've made a resource with NPCs and it works with 200ms-timer and it's a bit laggy even playing with my friend on local network. May be I need to use onClientPreRender or clientside timer to check state of peds? But I think it would be better to check their states serverside to avoid any desync... P.S.: like in CrystalMV's NPC HLC Traffic - 200 ms for serverside 'lightweight' update for unsynced peds & onClientRender for synced Edited February 9, 2021 by Thomas_Nightfire Link to comment
Moderators IIYAMA Posted February 10, 2021 Moderators Share Posted February 10, 2021 18 hours ago, Thomas_Nightfire said: Thanks a lot, undertood. But to not make another topic, how can I update peds frequently? I mean serverside timer with ~100ms will be bad, like u said earlier. I've made a resource with NPCs and it works with 200ms-timer and it's a bit laggy even playing with my friend on local network. May be I need to use onClientPreRender or clientside timer to check state of peds? But I think it would be better to check their states serverside to avoid any desync... P.S.: like in CrystalMV's NPC HLC Traffic - 200 ms for serverside 'lightweight' update for unsynced peds & onClientRender for synced The data rate is indeed an issue. I capped my own ped resource on 300ms. But the quantity also plays a role. It is bad practice to send the same information twice. If the client knows a ped is running, the server does not have to send that information again. If there are no peds around you, the data rate by this resource should be nearly 0. 19 hours ago, Thomas_Nightfire said: P.S.: like in CrystalMV's NPC HLC Traffic - 200 ms for serverside 'lightweight' update for unsynced peds & onClientRender for synced Are you sure that it sends data on the onClientRender rate? Link to comment
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