Jump to content

Trigger/tables


Recommended Posts

  • Scripting Moderators

Hi.

I'm creating custom data system based on tables client/server side (player interface/storing values which should be same for all of the players), trigger (changing those data, to keep them synced for all of players, in nearly all cases), and sqlite. (storing data after player quit; i'll implement soon). As i said in past that i don't wanna use setElementData at all i decided to do custom system for this.

I have two questions, which are important, to avoid any bugs.

1. Player inventory/items.

- Let's say i keep player items synced with client and server side. For GUI and for server in case of player death. Will be safer and better to change values by server only, and not by 1. client, 2. server?

In that way - like wiki says, it's better if server would do it?:

--> Synced item
--> triggerServerEvent to change it... Value on server side: sync complete --> Send triggerClientEvent back to player, refresh item count in GUI.

And not like that:

--> Synced item
--> Change item count on client side GUI... --> Send triggerServerEvent to sync it on server.

2. Refreshing GUI for group of players.

To refresh GUI for players in same colshape, i should do it after sync item value on server-side?

@IIYAMA i believe you can answer my questions :P

 

Link to comment

The first question basically depends on whether your players will tolerate lag (hint: GUI is supposed to be responsive). If it's something like GUI, it's best to update it on the client as if it was received by the server but set a timer to revert the GUI to server-state unless you hear back an acknowledgement event from the server (i.e. that the operation was executed without error) (don't unconditionally accept any sync event, in addition to any procedural checks (making sure the data is in valid format, etc) and so on, always safeguard and send back a kind of not-acceptable acknowledgement event when the event isn't accepted by the server for, among other, anti-cheat purposes).

For stuff which could give an unfair advantage or other bonuses/cheats like godmode, it would definitely be better to run the request by the server and back before any client changes are actually shown. Instead of instant change, you could show a spinning progress icon until you hear an acknowledgement event from the server (i.e. that the operation was executed without error)

Higher ping will make GUIs seem less responsive if you run it by the server before actually making changes visible. Additionally, make sure to saveguard the server from accepting illegitimate sync events, so clients can't change their--or others'--data on a whim.

Refreshing GUI for players in same colshape would be best done on client receive sync packet, individually per player if possible, or refresh the whole list when one player's data changes.

Edited by Investor
  • Like 3
Link to comment
  • Moderators

@majqq

The basic

The most safest way is like php does it. Sending an instruction to serverside. Serverside updates the list and send it back. During the proces the browser is refreshing the page, which is more or less freezing the interface.

This instruction is not the whole list of items. But it contains the identifiers of the items that are going to change + how they are going to change.

 

Enchantments

It is not idealistic to freeze the interface. In case of multiple users editing the same data. If your players have good internet, then you might not even need an enchantment.

 

But be warned, enchantments on top of sync items between server and multiple clients is complex.

 

You can update the clientside data first.

Instruction:

Item with id 253. Move to index 30.

 

Clientside

Do the instruction

Send the instruction to serverside

 

Serverside

Do the instruction

IF successful

 sync instruction (other clients)

ELSE If different outcome (A different client updated something before you did)

 Resend list to clientside (you)

 sync instruction (other clients)

ELSE

   Resend list to clientside (you) or undo instruction in case of not making any other changes.

 

 

 

 

 

 

 

  • Like 1
Link to comment
  • Scripting Moderators
2 hours ago, IIYAMA said:

@majqq

The basic

The most safest way is like php does it. Sending an instruction to serverside. Serverside updates the list and send it back. During the proces the browser is refreshing the page, which is more or less freezing the interface.

This instruction is not the whole list of items. But it contains the identifiers of the items that are going to change + how they are going to change.

 

Enchantments

It is not idealistic to freeze the interface. In case of multiple users editing the same data. If your players have good internet, then you might not even need an enchantment.

 

But be warned, enchantments on top of sync items between server and multiple clients is complex.

 

You can update the clientside data first.

Instruction:

Item with id 253. Move to index 30.

 

Clientside

Do the instruction

Send the instruction to serverside

 

Serverside

Do the instruction

IF successful

 sync instruction (other clients)

ELSE If different outcome (A different client updated something before you did)

 Resend list to clientside (you)

 sync instruction (other clients)

ELSE

   Resend list to clientside (you) or undo instruction in case of not making any other changes.

 

 

 

 

 

 

 

I'm not even thinking about browser. Just simple things: text labels, edits, checkboxes, comboboxes. What about adding server-side check if any trigger happend when editing, and if so, inform about that client, and block incoming trigger with data update?

Link to comment
  • Moderators
24 minutes ago, majqq said:

I'm not even thinking about browser. Just simple things: text labels, edits, checkboxes, comboboxes. What about adding server-side check if any trigger happend when editing, and if so, inform about that client, and block incoming trigger with data update?

 

The browser is a very different interface, but is facing the exact same issues.

 

Checkbox, radio buttons = last incoming message wins

After every change, you should use an increased int value to make sure that very old messages are not valid. This can also be done with timestamp. Luckily tcp is used, else it will be a really mess with the messages order.

 

Text / label = claiming the focus right. The first player can edit. The other players do have disabled inputs and even get kicked out of the field (which is reset to the last known server value). And yes blocking messages from players that have no focus rights.

An enchantment on to text/labels is:

- Updating the text while typing. 

- Showing who is editing.

 

 

 

 

 

 

  • Like 1
Link to comment
  • Scripting Moderators
On 03/05/2019 at 12:50, IIYAMA said:

 

The browser is a very different interface, but is facing the exact same issues.

 

Checkbox, radio buttons = last incoming message wins

After every change, you should use an increased int value to make sure that very old messages are not valid. This can also be done with timestamp. Luckily tcp is used, else it will be a really mess with the messages order.

 

Text / label = claiming the focus right. The first player can edit. The other players do have disabled inputs and even get kicked out of the field (which is reset to the last known server value). And yes blocking messages from players that have no focus rights.

An enchantment on to text/labels is:

- Updating the text while typing. 

- Showing who is editing.

 

 

 

 

 

 

What about player ammo, which also counts as an item, and count of it change after each time when player shoot. I'm just wondering how this should be done for custom data system.

This how it's looks with elementData:

-- client

function decreaseWeaponAmmo(weapon, ammo, ammoInClip, hitX, hitY, hitZ, hitElement)
	if source == getLocalPlayer() then
		local weapon = getElementData(getLocalPlayer(), "player:selected")
		if playerWeapons[weapon] then
			if getElementData(getLocalPlayer(), playerWeapons[weapon].ammo) > 0 and getPedWeaponSlot(getLocalPlayer()) == playerWeapons[weapon].wpSlot and getElementData(getLocalPlayer(), "player:weapon_1") == weapon then
				setElementData(getLocalPlayer(), playerWeapons[weapon].ammo, getElementData(getLocalPlayer(), playerWeapons[weapon].ammo) - 1 or 0)
			end
		end
	end
end
addEventHandler("onClientPlayerWeaponFire", getLocalPlayer(), decreaseWeaponAmmo)

 

1. From server, onPlayerWeaponFire, send back to player, refresh ammo in HUD/GUI.

2. From client, onClientPlayerWeaponFire - refresh ammo in HUD/GUI, send back to server.

3. Both, onClientPlayerWeaponFire and onPlayerWeaponFire.

 

And also a question related with onPlayerWeaponFire, wiki says: 

This does not trigger for projectiles, melee weapons, or camera. This event works only with weapons which have enabled bullet sync.

I'm not sure, but does RPG counts to those weapons which trigger this server event?

Edited by majqq
Link to comment
  • Moderators
4 hours ago, majqq said:

I'm not sure, but does RPG counts to those weapons which trigger this server event? 

RPG counts as weapon that fires bullets. It triggers the event atleast at clientside. Not sure if it also triggers serverside.

If not, then trigger the event manually. (It is not as if you can fire a RPG rapidly.)

 

About triggering client to server every time a player shoots.

This was a part of something I build a long time ago. It was a script that would sending bullet lines over to serverside. And detect if it hit players.

More or less how modern games do their damage model. That is for the onces that actually do the detection serverside.

But the issue with that was that it strongly relied on fast internet. If this criteria was not met, I was forced to freeze those players(high packet loss) and that made it even more unplayable for those.

(onPlayerWeaponFire was not available yet)

If those players where not frozen, they would teleport around everytime they shot a few bullets. TriggerServerEvent's are using TCP. Which is not streaming. Only 1 message can be send and received at the sametime. + causing the network to be blocked all the time.

 

 

Sending Message from client to server

Server receives

Server sends a message back.(message: yes it has been delivered)

Client sends next message to the server.

 

Well that was my issue/story.

 

My advice:

Fake the hud information.

Do only sync when it is absolutely necessary.

 

 

 

 

Edited by IIYAMA
  • Like 1
Link to comment
  • Scripting Moderators
1 hour ago, IIYAMA said:

RPG counts as weapon that fires bullets. It triggers the event atleast at clientside. Not sure if it also triggers serverside.

If not, then trigger the event manually. (It is not as if you can fire a RPG rapidly.)

 

About triggering client to server every time a player shoots.

This was a part of something I build a long time ago. It was a script that would sending bullet lines over to serverside. And detect if it hit players.

More or less how modern games do their damage model. That is for the onces that actually do the detection serverside.

But the issue with that was that it strongly relied on fast internet. If this criteria was not met, I was forced to freeze those players(high packet loss) and that made it even more unplayable for those.

(onPlayerWeaponFire was not available yet)

If those players where not frozen, they would teleport around everytime they shot a few bullets. TriggerServerEvent's are using TCP. Which is not streaming. Only 1 message can be send and received at the sametime. + causing the network to be blocked all the time.

 

 

Sending Message from client to server

Server receives

Server sends a message back.(message: yes it has been delivered)

Client sends next message to the server.

 

Well that was my issue/story.

 

My advice:

Fake the hud information.

Do only sync when it is absolutely necessary.

 

 

 

 

Oh well :/

What if, i would change them that way: OnClientPlayerWeaponFire -> change data in client side table, so it will be actual ammo in HUD. OnPlayerWeaponFire -> change data in server-side table. Without any triggers. That way may cause any problems, f.e: other value on client, and other value on server side table?

Link to comment
  • Moderators
2 hours ago, majqq said:

Oh well :/

What if, i would change them that way: OnClientPlayerWeaponFire -> change data in client side table, so it will be actual ammo in HUD. OnPlayerWeaponFire -> change data in server-side table. Without any triggers. That way may cause any problems, f.e: other value on client, and other value on server side table?

What is your ultimate goal?

Being able to use m4 and ak47 within the same inventory?

In that case you could give just 999999 ammo per weapon.

 

When the table runs out of ammo clientside. Block the shooting button of that weapon. And send a signal to serverside that the weapon is empty.

 

Don't be me, with complex :~ that eats too much network.

 

 

 

Link to comment
  • Scripting Moderators
On 06/05/2019 at 12:42, IIYAMA said:

What is your ultimate goal?

Being able to use m4 and ak47 within the same inventory?

In that case you could give just 999999 ammo per weapon.

 

When the table runs out of ammo clientside. Block the shooting button of that weapon. And send a signal to serverside that the weapon is empty.

 

Don't be me, with complex :~ that eats too much network.

 

 

 

Probably i would need to test all the ways.

If i would keep ammo value synced only on client, and want to send it back when player quit within 'OnClientResourceStop', it should work? And what about if player lost connection, trigger will be sent?

Related topic: 

 

I didn't test it yet, because i need to finish other things, but i want to be sure, for future.

Edited by majqq
Link to comment
  • Moderators
41 minutes ago, majqq said:

If i would keep ammo value synced only on client, and want to send it back when player quit within 'OnClientResourceStop', it should work? And what about if player lost connection, trigger will be sent? 

You could load a small xml file on to the client his computer. (The whole time)

When the player reconnects, subtract the ammo that hasn't been synced. (Sync once in a while with the server)

Ofcourse add a security layer. Which will prevent players to edit those values. If the file has a mismatch with the account data of the player. Make sure to punish him by deleting all the ammo.

 

Why xml and not a default file?

Because you can edit parts of it. And not have to recreating the whole file when there are changes. (And yes for the XML haters, you can also use JSON in XML... cry!)

Edited by IIYAMA
  • Like 1
  • Thanks 1
Link to comment
  • Scripting Moderators
On 09/05/2019 at 10:47, IIYAMA said:

You could load a small xml file on to the client his computer. (The whole time)

When the player reconnects, subtract the ammo that hasn't been synced. (Sync once in a while with the server)

Ofcourse add a security layer. Which will prevent players to edit those values. If the file has a mismatch with the account data of the player. Make sure to punish him by deleting all the ammo.

 

Why xml and not a default file?

Because you can edit parts of it. And not have to recreating the whole file when there are changes. (And yes for the XML haters, you can also use JSON in XML... cry!)

By the way i should add "delay" (getTickCount()/setTimer), protection for events which player can trigger manually? I'm talking about events which send small count of data. Just wanna be sure if this can overload server too.

Link to comment
  • Moderators
2 hours ago, majqq said:

By the way i should add "delay" (getTickCount()/setTimer), protection for events which player can trigger manually? I'm talking about events which send small count of data. Just wanna be sure if this can overload server too.

Yes

In case of database functions connected to it, it is very recommended. Filereading and writing (even with a database) is a very slow operation.

If a player spams commands that are requesting database info, the HDD/SSD will be busy doing their slow operations. In case of an SSD the issue is partly solved. But even so it is a SSD, the response time is not really comparable with the speed of ram.

----

Putting limits on non db operations as well?

Yes,

The event system will have a hard time.

The network packet system will have a bit harder time, but there is a messages buffer limit on clientside. Everything goes over TCP, so only one (trigger) message can be send per request.

-----

With no block, does it bother other player directly? (Without the server having a hard time)

Possible,

If for example the function setElementPosition is used serverside, all players in the server will receive data from it.

  • Like 1
Link to comment
  • Scripting Moderators
On 09/05/2019 at 10:47, IIYAMA said:

You could load a small xml file on to the client his computer. (The whole time)

When the player reconnects, subtract the ammo that hasn't been synced. (Sync once in a while with the server)

Ofcourse add a security layer. Which will prevent players to edit those values. If the file has a mismatch with the account data of the player. Make sure to punish him by deleting all the ammo.

 

Why xml and not a default file?

Because you can edit parts of it. And not have to recreating the whole file when there are changes. (And yes for the XML haters, you can also use JSON in XML... cry!)

Probably ain't gonna use it., the problem will be in case of clearing files from cache.

24 minutes ago, IIYAMA said:

Yes

In case of database functions connected to it, it is very recommended. Filereading and writing (even with a database) is a very slow operation.

If a player spams commands that are requesting database info, the HDD/SSD will be busy doing their slow operations. In case of an SSD the issue is partly solved. But even so it is a SSD, the response time is not really comparable with the speed of ram.

----

Putting limits on non db operations as well?

Yes,

The event system will have a hard time.

The network packet system will have a bit harder time, but there is a messages buffer limit on clientside. Everything goes over TCP, so only one (trigger) message can be send per request.

-----

With no block, does it bother other player directly? (Without the server having a hard time)

Possible,

If for example the function setElementPosition is used serverside, all players in the server will receive data from it.

By the way thank you for all of your support and advices, you're helping me very much :D

  • Like 1
Link to comment
  • Scripting Moderators
On 13/05/2019 at 08:58, IIYAMA said:

Yes

In case of database functions connected to it, it is very recommended. Filereading and writing (even with a database) is a very slow operation.

If a player spams commands that are requesting database info, the HDD/SSD will be busy doing their slow operations. In case of an SSD the issue is partly solved. But even so it is a SSD, the response time is not really comparable with the speed of ram.

----

Putting limits on non db operations as well?

Yes,

The event system will have a hard time.

The network packet system will have a bit harder time, but there is a messages buffer limit on clientside. Everything goes over TCP, so only one (trigger) message can be send per request.

-----

With no block, does it bother other player directly? (Without the server having a hard time)

Possible,

If for example the function setElementPosition is used serverside, all players in the server will receive data from it.

About that limit in client side, it means that client can only send one trigger per request? And there's no limit on server-side?

Link to comment
  • Moderators
13 minutes ago, majqq said:

About that limit in client side, it means that client can only send one trigger per request? 

Technically yes, as other wise the message order gets mixed up. The implementation of http2 could help, but I have no idea if that is implemented for the the trigger events. It is at least used for the steamer.

So if you want to know how this works, I suggest asking a developer about it.

I am making a lot of assumptions, but the one that build it might give you the exact details.

Note clients their traffic shouldn't block each other, unless the server is out of bandwidth.

 

 

 

 

 

  • Like 1
Link to comment
  • 1 month later...
  • Scripting Moderators
On 18/05/2019 at 11:34, IIYAMA said:

Technically yes, as other wise the message order gets mixed up. The implementation of http2 could help, but I have no idea if that is implemented for the the trigger events. It is at least used for the steamer.

So if you want to know how this works, I suggest asking a developer about it.

I am making a lot of assumptions, but the one that build it might give you the exact details.

Note clients their traffic shouldn't block each other, unless the server is out of bandwidth.

 

 

 

 

 

Hey, IIYAMA, i've tested both, onClientPlayerWeaponFire, and onPlayerWeaponFire, i would like to use them for sync. The problem is that server-side variation of this event doesn't trigger for RPG. I thought about solving this by adding condition which checks if weapon == RPG and then send trigger to server, and sync. I am not sure if this would be best solution, that's why i'm asking you about it.

Link to comment
  • Moderators
11 hours ago, majqq said:

Hey, IIYAMA, i've tested both, onClientPlayerWeaponFire, and onPlayerWeaponFire, i would like to use them for sync. The problem is that server-side variation of this event doesn't trigger for RPG. I thought about solving this by adding condition which checks if weapon == RPG and then send trigger to server, and sync. I am not sure if this would be best solution, that's why i'm asking you about it.

It is fine, as it's fire rate is slow,

just keep in mind that:

- It might can create a tiny bit more lag for a player with bad internet connection.

- The network message order is dominating over the others. (Faster)

 

Edited by IIYAMA
  • Like 1
Link to comment
  • 1 month later...
  • Scripting Moderators
On 24/06/2019 at 19:23, IIYAMA said:

It is fine, as it's fire rate is slow,

just keep in mind that:

- It might can create a tiny bit more lag for a player with bad internet connection.

- The network message order is dominating over the others. (Faster)

 

Hey once again :D

I don't wanna create new topic for this, and question is related to trigger/tables somehow.

So once again stricte related to `network buffer`. I've read some explanation of one of the server owners, which probably use such a thing for reduce bandwith usage and so (custom data system for sure, not element data). I believe that helps a lot, and i saw much difference in server performance, when using tables + buffer.

And now i'm gonna introduce it for everything else aka player health, player weapon etc. Furthermore, i created a utility which allows me to adjust delay or latent event directly in game (to have smooth gameplay). And here comes the question, let's say i have synced players weapons for everyone, so i directly know what damage weapon have. Should i `fake HUD information` when we're talking about player health level? I remember you adviced me to do so, in case above. It was explained that way:

- After taking damage, health is decreased on client-side (probably to avoid instant death due to latency, and inform player to heal - this isn't the real value all the time, server hold real and current health value?)

- Later client send trigger to server about incoming damage. As we shouldn't trust client, server do same step but on his own (calculate health - damage), change health value and send back to client.

Link to comment
  • Scripting Moderators
On 24/06/2019 at 19:23, IIYAMA said:

It is fine, as it's fire rate is slow,

just keep in mind that:

- It might can create a tiny bit more lag for a player with bad internet connection.

- The network message order is dominating over the others. (Faster)

 

Besides, i need to ask you about another thing.

Once, you told me and show the way for checking incoming data (for example if it is actually a number).

- Should i use:

type

or

tonumber()

Or both ways are correct?

At the moment it looks like this:

local c_buffer, s_buffer, c_latent, s_latent = c_data[1], c_data[2], c_data[3], c_data[4]
if c_buffer and tonumber(c_buffer) and tonumber(c_buffer) >= 0 and s_buffer and tonumber(s_buffer) and tonumber(s_buffer) >= 0 then
	--
end

 

Edited by majqq
Link to comment
  • Moderators
12 minutes ago, majqq said:

Or both ways are correct?

 

 

Both can be used.

 

 

 

if type(value) == "number" then
  
end

 

 

Just tonumber is more forgiving, as you can also accept strings by converting them to numbers.

local value = "123"

value = tonumber(value)
if value then
  
end

 

 

It just depends how forgiving you want to be.

 


 

 

Do not do this: (2 function calls)

if tonumber(c_buffer) and tonumber(c_buffer) >= 0  then
  
end

 

 

But do this: (1 function call)

c_buffer = tonumber(c_buffer)
if c_buffer and c_buffer >= 0 then
  
end

 

If you can reduce function calls, by just writing it a bit different without messing with the programming flow, you should take it.

 

  • Like 2
Link to comment
  • Scripting Moderators
7 minutes ago, IIYAMA said:

 

 

Both can be used.

 

 

 


if type(value) == "number" then
  
end

 

 

Just tonumber is more forgiving, as you can also accept strings by converting them to numbers.


local value = "123"

value = tonumber(value)
if value then
  
end

 

 

It just depends how forgiving you want to be.

 


 

 

Do not do this: (2 function calls)


if tonumber(c_buffer) and tonumber(c_buffer) >= 0  then
  
end

 

 

But do this: (1 function call)


c_buffer = tonumber(c_buffer)
if c_buffer and c_buffer >= 0 then
  
end

 

If you can reduce function calls, by just writing it a bit different without messing with the programming flow, you should take it.

 

Thanks.

Did you read my earlier post? Because I cover all my hopes in that system. What you would say about that? 

 

Link to comment
  • Moderators
On 03/08/2019 at 10:30, majqq said:

And now i'm gonna introduce it for everything else aka player health, player weapon etc. Furthermore, i created a utility which allows me to adjust delay or latent event directly in game (to have smooth gameplay). And here comes the question, let's say i have synced players weapons for everyone, so i directly know what damage weapon have. Should i `fake HUD information` when we're talking about player health level? I remember you adviced me to do so, in case above. It was explained that way:

- After taking damage, health is decreased on client-side (probably to avoid instant death due to latency, and inform player to heal - this isn't the real value all the time, server hold real and current health value?)

- Later client send trigger to server about incoming damage. As we shouldn't trust client, server do same step but on his own (calculate health - damage), change health value and send back to client.

 

I can't remembered which thing was responsible for the damage. But the way you describe it, it doesn't sounds too bad.

Yes, faking will make the game feel more smoother, especially when we are talking about slow updates.

But it will remain an trial-and-error case, as you will have to test it with your users. There are always some unpredictable situations which you didn't think of during your development.

 

Did I answer all your questions? Or did I miss anything?

Edited by IIYAMA
  • Like 1
Link to comment
  • Scripting Moderators
12 hours ago, IIYAMA said:

 

I can't remembered which thing was responsible for the damage. But the way you describe it, it doesn't sounds too bad.

Yes, faking will make the game feel more smoother, especially when we are talking about slow updates.

But it will remain an trial-and-error case, as you will have to test it with your users. There are always some unpredictable situations which you didn't think of during your development.

 

Did I answer all your questions? Or did I miss anything?

Yes, thank you so much :D

  • Like 1
Link to comment
  • Scripting Moderators
On 05/08/2019 at 17:29, IIYAMA said:

 

I can't remembered which thing was responsible for the damage. But the way you describe it, it doesn't sounds too bad.

Yes, faking will make the game feel more smoother, especially when we are talking about slow updates.

But it will remain an trial-and-error case, as you will have to test it with your users. There are always some unpredictable situations which you didn't think of during your development.

 

Did I answer all your questions? Or did I miss anything?

Matter of time, and next questions coming :P

Next question i have is about sending data after receiving trigger from client.

local string, string_2 = incoming_data[1], incoming_data[2] -- incoming data from client
if string and string_2 then -- ~= nil/boolean
	if my_table and #my_table > 0 then -- continue only if table size > 0
		for i = 1, #my_table do
			local existing_string = my_table[i][1]
			if existing_string == string then -- replace data
				my_table[i] = {string_2, string_2}
				triggerLatentClientEvent("sendDataToClient", resourceRoot, player, {my_table}) -- send whole actual table with data
				break
			end
		end
	end
end

Take a look at example code, which is related to mine. What i want to say it's good habit to send whole table with updated data instead of sending only certain indexes? Just in case of questions - table size will have <= 50 indexes.

Link to comment

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...