Hello!
At the moment, I'm working on a simple, non persistant money system. I have started to look at meta functions. Here is what I've got so far:
In sh_player.lua
[code]
local meta = FindMetaTable( "Player" )
function meta:GetMoney()
return self.Money
end
function meta:SetMoney( amount )
self.Money = amount
end
function meta:AddMoney( amount )
self:SetMoney( self:GetMoney() + amount )
end
function meta:CanAfford( amount )
if(self:GetMoney() >= amount) then
return true
else
return false
end
end
[/code]
And in init.lua
[code]
function GM:PlayerInitialSpawn( ply )
local StartMoney = 1
ply:SetTeam( 1 )
ply:ConCommand( "team_menu" )
if( ply:GetMoney() == nil) then
ply:SetMoney( StartMoney )
end
print( "I have", ply:GetMoney() )
end
[/code]
This all works fine, I have the right amount of money when I spawn. After this, though, I get a bit lost.
[code]
function PoliceWages( ply )
ply:AddMoney(1)
end
timer.Create( "PoliceWages", 5, 0, PoliceWages )
[/code]
Here is a function I've been trying to make, to pay the player every five seconds. Obviously, I haven't defined what ply actually is, so the console throws up the 'failed to index nil value' error. The problem is, I don't really have any idea how to actually do this. I mean, for some functions, I just don't get any trouble. Like
[code]
function team_1( ply )
ply:SetTeam( 1 )
end
concommand.Add( "team_1", team_1 )
[/code]
Just works. It must be something simple, but I don't get it.
Another issue is that I can't seem to get the money system stuff to interface properly with the HUD I'm making.
[code]
local moneyText = LocalPlayer():GetMoney()
draw.SimpleText(moneyText, "default", ScrW()*0.017, ScrH()*0.98, Color(255,255,255,255), TEXT_ALIGN_RIGHT )
[/code]
Just returns text saying 'nil'. I'm guessing this is because I'm using LocalPlayer? But I don't know what I could put in it's place.
[lua]local meta = FindMetaTable( "Player" )
local StartMoney = 1
function meta:GetMoney()
return self.Money or 0
end
function meta:SetMoney( amount )
self.Money = amount
end
function meta:AddMoney( amount )
self:SetMoney( self:GetMoney() + amount )
end
function meta:CanAfford( amount )
return self:GetMoney() >= amount
end
function GM:PlayerInitialSpawn( ply )
ply:SetTeam( 1 )
ply:ConCommand( "team_menu" )
ply:SetMoney(StartMoney)
print( "I have ", ply:GetMoney() )
timer.Create(ply:SteamID().."Wages", 5, 0, function()
ply:AddMoney(1)
print( "I have ", ply:GetMoney() )
end)
end[/lua]
Put this stuff serverside. Sorry for the fucked up spacing, I don't have access to an editor right now. This is assuming you're going to individually pay players. If you're going to pay all players then you'd need to do this a little different.
[QUOTE=ChiefNinja;44633319]
[code]
function PoliceWages( ply )
ply:AddMoney(1)
end
timer.Create( "PoliceWages", 5, 0, PoliceWages )
[/code]
Here is a function I've been trying to make, to pay the player every five seconds. Obviously, I haven't defined what ply actually is, so the console throws up the 'failed to index nil value' error. The problem is, I don't really have any idea how to actually do this.
[/QUOTE]
Think about it. If you're trying to pay all police ever 5 seconds, you should probably just loop through all players and check if they are police, if they are give them some $$$. So
[LUA]
local function PoliceWages() -- localized functions/variables are better practice, unless you have a reason not to
for _, ply in pairs( player.GetAll() ) do -- loop through all players, define ply as the player we are currently at in the loop
if ply:IsPolice() then -- I don't know how you're defining police, in darkrp it's ply:isCP(). you could also check the team like ( if ply:Team() == 1 then )
ply:AddMoney( 1 ) -- give them the money
end -- close the if statement
end -- end the loop
end
timer.Create( "PoliceWages", 5, 0, PoliceWages ) -- every 5 seconds, call the function PoliceWages
[/LUA]
[QUOTE]
[code]
function team_1( ply )
ply:SetTeam( 1 )
end
concommand.Add( "team_1", team_1 )
[/code]
Just works. It must be something simple, but I don't get it.
[/QUOTE]
it's because you're using the function team_1 as the function to call when the concommand is called, so it will pass the arguments to it.
It's essentially the same as doing
[LUA]
concommand.Add( "team_1", function( ply, cmd, args ) team_1( ply, cmd, args ) end )
[/LUA]
[QUOTE]
Another issue is that I can't seem to get the money system stuff to interface properly with the HUD I'm making.
[code]
local moneyText = LocalPlayer():GetMoney()
draw.SimpleText(moneyText, "default", ScrW()*0.017, ScrH()*0.98, Color(255,255,255,255), TEXT_ALIGN_RIGHT )
[/code]
Just returns text saying 'nil'. I'm guessing this is because I'm using LocalPlayer? But I don't know what I could put in it's place.[/QUOTE]
it's because you've only set the money variable serverside. the client (the players) have no way of knowing what the players money is. So while the function might exist in both client and server realms, the variable isn't automatically networked. You need to do this yourself, using the net library
[url]http://wiki.garrysmod.com/page/Net_Library_Usage[/url]
good luck, and feel free to ask any questions about networking/anything
Ah! So this is where NWInts come in :smile:
I figured that since sh_player was 'included' in init and cl_init I could just use it server-side.
[QUOTE=ChiefNinja;44634179]Ah! So this is where NWInts come in :smile:
I figured that since sh_player was 'included' in init and cl_init I could just use it server-side.[/QUOTE]
Don't put anything like money management in a shared file. The only thing the client really needs is GetMoney and CanAfford. Always keep important functions serverside, and always verify things serverside. Never trust the client.
Okay, I saw another guy on here who put all of the important functions in an 'if SERVER' bit of code. Would this stop clients from accessing it?
[QUOTE=ChiefNinja;44634700]Okay, I saw another guy on here who put all of the important functions in an 'if SERVER' bit of code. Would this stop clients from accessing it?[/QUOTE]
The client has access to the entire file if it is sent to them.
If your server is the only thing that has control over saving the information, and managing it, then it won't matter what they try to set it client-side. Don't rely on the client to tell you the price of things, don't rely on the client to tell you how much money they have. As long as you don't trust user input, then it won't matter what they try to spoof it as. The client will see one number, the server will see another. When the server updates the amount, it should update the clients spoofed number back to normal depending what they did.
[QUOTE=ChiefNinja;44634700]Okay, I saw another guy on here who put all of the important functions in an 'if SERVER' bit of code. Would this stop clients from accessing it?[/QUOTE]
Just stuff the hook and important functions into a serverside file and they won't be sent the file at all.
Oh, to add to the if ( SERVER ) then end thing. If the code is essentially identical on both the client and server; that's when you want to use that. If the server-side logic is completely different to where you're not repeating a large chunk of code; then make a server only file so the client won't see the inner-workings.
Cool, so I'm thinking I'll split it into two files. One with all the boring stuff like GetMoney() and CanAfford() and I'll put all the top secret, fancy AddMoney() stuff on a strictly server side file?
I'd recommend this technique for you since you're learning lua (this isn't the folder structure technique i use now but it was helpful when I was learning):
Make three folders one called server, one called shared, and one called client.
make files called sv_econ and sh_econ in the server and shared folders respectively. Put all your server side econ stuff that stores and loads data in the sv_econ file. Put all your get and set functions in sh_econ that way both the server and the client can view them.
You should put your huds, gui's and anything else that is purely clientside in the client folder.
Using separate folders for separate domains (server and client, shared being a constructed domain just meaning both server and client) will help you keep code separate conceptually by physically dividing the spaces.
Both server and client can access shared folder but client can't access server and server can't access client.
(i imagine this is a bit more indepth than you needed but I havn't posted in a while and I got a bit carried away)
Cheers! Good idea.
Well, sorry to bring this thread back to life, but I've hit another issue.
I've got the money system up and (just about) running. The problem comes when interfacing with the client.
[code]
local meta = FindMetaTable( "Player" )
function meta:GetMoney()
return self.Money or 0
end
function meta:CanAfford( amount )
if(self:GetMoney() >= amount) then
return true
else
return false
end
end
function meta:SetMoney( amount )
self.Money = amount
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
for _, ply in pairs( player.GetAll() ) do
net.WriteInt( ply:GetMoney(), 32 )
end
net.Broadcast()
end
function meta:AddMoney( amount )
self:SetMoney( self:GetMoney() + amount )
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
for _, ply in pairs( player.GetAll() ) do
net.WriteInt( ply:GetMoney(), 32 )
end
net.Broadcast()
end
[/code]
and in cl_init.lua
[code]
net.Receive( "ply_GetMoney", function( length, client )
Money = net.ReadInt( 32 )
end )
[/code]
This system works fine in singleplayer, but when I tested it out with someone in multiplayer, the money variable = my money, not theirs. Any ideas?
You're iterating though the player list and then calling net.WriteInt for every player, meaning the first net.ReadInt will return the value from the player with the lowest ID in the server, which will likely be you.
I don't understand why you don't want to use SetNWInt instead of this method. Is there a problem with security?
Why does every player need to know about every other player? The player should only know about themself; if they do a trade then the server shouldn't allow them to show the other player more money in a trade window than they have, for example; and the client-side for the player trying to dupe the system should only allow a maximum up to what they have. In the event they cheat-engine and change the value, the server will prevent the other player from seeing the fraud...
Cheat engine couldn't change the value that would be displayed to other players in any case since it can't edit server side values (unless you're doing something rediculously retarded) but in any case there really isn't much risk in showing everyone everyone else's money in the case that you might need to know. Money is hardly secretive data nor is it's exposure seriously damaging.
Your only real concern is data bandwidth which, when syncing ints, is really insignificant (though the key string's length may be some source of concern it's really not major...)
I'd strongly recommend using networked ints honestly, they're just easiest. At some point it'd be nice to create a library for private networked fields.
What's the performance on networked variables like? The wiki seems to say they're pretty expensive...
[editline]30th April 2014[/editline]
Is there a way for me to send the net message to the player the function was called on, maybe?
Networked variables are pretty damn taxing, just because of how they were written. Someone wrote a module that does basically the same thing but is much more efficient, let me see if I can find it...
[url]http://facepunch.com/showthread.php?t=1383088[/url]
Well, I've decided I'm going to keep trying net messages for now.
I think I either need to make it so the meta functions detect who they are being called on, and net.send to the player accordingly (I don't know how to do this) or work it so the client somehow detects who sent the message, and sets the clientside money variable accordingly. Net messages don't seem to be able to tell the client who they are sent from. Any help?
Also, how are net messages vs NWints in terms of performance?
Just net.WriteEntity( ply ) ( ply being the player you are sending information about ) in the server-side net message to the player.
I'm not sure about the performance, but I believe that I remember talk of NWVars using user messages, which are depreciated.
[QUOTE=ChiefNinja;44686158]Well, sorry to bring this thread back to life, but I've hit another issue.
I've got the money system up and (just about) running. The problem comes when interfacing with the client.
[code]
local meta = FindMetaTable( "Player" )
function meta:GetMoney()
return self.Money or 0
end
function meta:CanAfford( amount )
if(self:GetMoney() >= amount) then
return true
else
return false
end
end
function meta:SetMoney( amount )
self.Money = amount
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
net.WriteInt( self:GetMoney(), 32 )
net.Send( self )
end
function meta:AddMoney( amount )
self:SetMoney( self:GetMoney() + amount )
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
net.WriteInt( self:GetMoney(), 32 )
net.Send( self )
end
[/code]
and in cl_init.lua
[code]
net.Receive( "ply_GetMoney", function( length, client )
Money = net.ReadInt( 32 )
end )
[/code]
This system works fine in singleplayer, but when I tested it out with someone in multiplayer, the money variable = my money, not theirs. Any ideas?[/QUOTE]
[code]
local meta = FindMetaTable( "Player" )
function meta:GetMoney()
return self.Money or 0
end
function meta:CanAfford( amount )
if(self:GetMoney() >= amount) then
return true
else
return false
end
end
function meta:SetMoney( amount )
self.Money = amount
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
net.WriteInt( self:GetMoney(), 32 )
net.Send( self )
end
function meta:AddMoney( amount )
self:SetMoney( self:GetMoney() + amount )
util.AddNetworkString( "ply_GetMoney" )
net.Start( "ply_GetMoney" )
net.WriteInt( self:GetMoney(), 32 )
net.Send( self )
end
[/code]
Pfft. Can't beleive I didn't figure that out :v: Cheers!
Sorry, you need to Log In to post a reply to this thread.