Reserved slots
<div class="IPBDescription">For those admins who want to be sure of playing</div>Ok, I have been messing a little in ns2 lua, and I got a decent result. This is a script that has a table of VIP (Very Important Person), and a number of reserved slots (also requires you type max slots, as I dunno functino to get that). It wont kick people who already play on the server, instead it will kick players joining a reserved slot unless they are VIP.
if a server owner has a 8 player server, he can set to actually be 9 where 1 slot is reserved, so he can join even when its "full", but play with more lag (9 players).
Also, I would be GLaD if you left my name in the VIP list ;).
You will have to edit the VIP list (remember , after each name, except for the last name), the maxSlots and the reservedSlots into what you want it to be.
This is the code, for some reason it requires a Script.Load("lua/filename.lua") in Server.lua.
And yes I use nickname, and I know its lousy security, but some people have dynamic ip (making using ip even more stupid, not to mention I dunno how to get a players ip :P), and ns2 doesnt support steamid yet or something (I really want it!).
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// ================================================================================
======
//
// lua\ReservedSlots.lua
//
// Created by: Feha
// Mod which reserves some slots for a list of VIP's
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
//Table of people with VIP status
local VIP = {
"Feha",
"YourOwnName"
}
//Table of people who already joined as the event I listen to run often, and therefor need a check if its the first time it runs.
local alreadyJoined = {}
//Amount of reserved slots and max slots
local reserverSlots = 2
local maxSlots = 8
//Function to get number of players on server
local function numPlayers()
//Cant find a good function for this, so I get the number of players in the playerlist
local numPlayers = #GetGamerules().playerList
return numPlayers
end
//This function check if a table contains a value, table.find returns index which I dont need, aswell as cares for multidimensional tables
local function HasValue(t, value)
for k,v in ipairs(t) do
if (v == value) then
return true
end
end
end
//Function that runs when a player connects
function OnClientConnect(client, name)
//Get the player entity of the client, so we can broadcast
local player = client:GetControllingPlayer()
if not player then return end
//Only adds the value into the table if it wasnt there before, and if it doesnt, we dont want to continue.
//Use client and not player, as everytime you change team, gestate or such, you get a new player instance assigned to your client
if not table.insertunique(alreadyJoined, client) then return end
name = string.gsub(name, "\"(.*)\"", "%1")
//Broadcast if the player is VIP or not, to make VIP feel important, and others realise they exist
if HasValue(VIP, name) then
Server.Broadcast(nil, string.format("%s %s", name, "is VIP!"))
else
Server.Broadcast(nil, string.format("%s %s", name, "is not VIP!"))
end
//ICheck if player tries to join a reserved slot
if (numPlayers() > (maxSlots-reserverSlots)) then
if HasValue(VIP, name) then
Server.Broadcast(nil, string.format("%s %s", name, "joined reserved slot"))
else
Server.Broadcast(nil, string.format("%s %s", name, "kicked for joining reserved slot"))
//Kick client from the server
Server.DisconnectClient(client)
//Server.SendCommand( player, "disconnect" ) //Slow on actually making the server drop the client
end
end
end
//Function that runs when a player disconnects
function OnClientDisconnect(client)
local player = client:GetControllingPlayer()
//If they disconnect and later reconnect, we have to check them again, which requires we remove them from this list
table.removevalue(alreadyJoined, client)
end
//Not used as player doesnt have a name when this runs
//Event.Hook("ClientConnect", OnClientConnect)
//This runs when a player joined, and is the same hook used to set players name, however, it runs quite often...
Event.Hook("Console_name", OnClientConnect)
//This runs when a player disconnects, which we need to listen so we remove them from the "alreadyjoined" player list
Event.Hook("ClientDisconnect", OnClientDisconnect)<!--c2--></div><!--ec2-->
if a server owner has a 8 player server, he can set to actually be 9 where 1 slot is reserved, so he can join even when its "full", but play with more lag (9 players).
Also, I would be GLaD if you left my name in the VIP list ;).
You will have to edit the VIP list (remember , after each name, except for the last name), the maxSlots and the reservedSlots into what you want it to be.
This is the code, for some reason it requires a Script.Load("lua/filename.lua") in Server.lua.
And yes I use nickname, and I know its lousy security, but some people have dynamic ip (making using ip even more stupid, not to mention I dunno how to get a players ip :P), and ns2 doesnt support steamid yet or something (I really want it!).
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// ================================================================================
======
//
// lua\ReservedSlots.lua
//
// Created by: Feha
// Mod which reserves some slots for a list of VIP's
//
// ========= For more information, visit us at http://www.unknownworlds.com =====================
//Table of people with VIP status
local VIP = {
"Feha",
"YourOwnName"
}
//Table of people who already joined as the event I listen to run often, and therefor need a check if its the first time it runs.
local alreadyJoined = {}
//Amount of reserved slots and max slots
local reserverSlots = 2
local maxSlots = 8
//Function to get number of players on server
local function numPlayers()
//Cant find a good function for this, so I get the number of players in the playerlist
local numPlayers = #GetGamerules().playerList
return numPlayers
end
//This function check if a table contains a value, table.find returns index which I dont need, aswell as cares for multidimensional tables
local function HasValue(t, value)
for k,v in ipairs(t) do
if (v == value) then
return true
end
end
end
//Function that runs when a player connects
function OnClientConnect(client, name)
//Get the player entity of the client, so we can broadcast
local player = client:GetControllingPlayer()
if not player then return end
//Only adds the value into the table if it wasnt there before, and if it doesnt, we dont want to continue.
//Use client and not player, as everytime you change team, gestate or such, you get a new player instance assigned to your client
if not table.insertunique(alreadyJoined, client) then return end
name = string.gsub(name, "\"(.*)\"", "%1")
//Broadcast if the player is VIP or not, to make VIP feel important, and others realise they exist
if HasValue(VIP, name) then
Server.Broadcast(nil, string.format("%s %s", name, "is VIP!"))
else
Server.Broadcast(nil, string.format("%s %s", name, "is not VIP!"))
end
//ICheck if player tries to join a reserved slot
if (numPlayers() > (maxSlots-reserverSlots)) then
if HasValue(VIP, name) then
Server.Broadcast(nil, string.format("%s %s", name, "joined reserved slot"))
else
Server.Broadcast(nil, string.format("%s %s", name, "kicked for joining reserved slot"))
//Kick client from the server
Server.DisconnectClient(client)
//Server.SendCommand( player, "disconnect" ) //Slow on actually making the server drop the client
end
end
end
//Function that runs when a player disconnects
function OnClientDisconnect(client)
local player = client:GetControllingPlayer()
//If they disconnect and later reconnect, we have to check them again, which requires we remove them from this list
table.removevalue(alreadyJoined, client)
end
//Not used as player doesnt have a name when this runs
//Event.Hook("ClientConnect", OnClientConnect)
//This runs when a player joined, and is the same hook used to set players name, however, it runs quite often...
Event.Hook("Console_name", OnClientConnect)
//This runs when a player disconnects, which we need to listen so we remove them from the "alreadyjoined" player list
Event.Hook("ClientDisconnect", OnClientDisconnect)<!--c2--></div><!--ec2-->
Comments
<!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->This is the code, for some reason it requires a Script.Load("lua/filename.lua") in Server.lua.<!--QuoteEnd--></div><!--QuoteEEnd-->
If for "filename.lua" you mean your script "ReservedSlots.lua", then that is perfectly simple to explain.
A bit of speculation ahead (I have access to the same documentation as you do after all):
Lua-scripts aren't loaded based on their presence in the Lua-directory, but rather because of their inclusion in other loaded scripts. This means your script has to be loaded (in this particular case) by "Server.lua", which in turn is loaded by another script (actually it isn't but let's for now assume it is). This continues until a top-level script is reached. With that I mean a script that is explicitely loaded by NS2. So you should imagine the Lua file-structure as a hierarchie that starts out small and broadens as it moves through the Lua-scripts. To my knowledge there are 3 Lua-VMs: Main, Client and Server. Each of these VMs have their own top-level script-file it explicitly loads:
Main VM: Main.lua
Client VM: Client.lua
Server VM: Server.lua (so in that example above "Server.lua" isn't actually included by another file, but already is the top-level script).
It should also be noted that a direct consequence of this is that whenever an integrity-check is performed on the Lua-files, or a new NS2-build is released, the Script.Load-reference you've added to ensure your scripts is parsed, will be removed and the mod is disabled yet again. Which means you have to re-include that Script.Load every new NS2-build. To avoid having it removed by an integrity-check, you can consider making it a mod (by that I mean put it in a seperate directory, and run it using -game).
Main VM: Main.lua
Client VM: Client.lua
Server VM: Server.lua (so in that example above "Server.lua" isn't actually included by another file, but already is the top-level script).<!--QuoteEnd--></div><!--QuoteEEnd-->
The Main and Client VMs were merged together about 5 builds ago or so. The Main.lua file is just Script.Load'ed from the Client.lua now
The names of the top level scripts for the Server and Client VM can be controlled with a custom game_setup.xml in your mods directory
I would also like an explanation of the bugs, might help me fix it.
Just saying there may be bugs because of this mod wont really help me :P.
I dont think there were any errors in the logs but not sure. When I connected at some point, the server was only have full and it seemed like players were getting kicked for joining a non-reserved slot. Also, players had their nickname with a (2) and or (3) behind them so Im not sure if it was your mod or some other bug in the server.
I will try it again on one server and see if I can come up with something more specific.
I blame the documentation for not telling me how to drop players serverside (I have to tell server to tell client to disconnect, which as I said is slightly bugged serverside).
About it kicking players when its half full, maybe you forgot changing the max slots and reserved slots variables? Documentation lacked info on how to get max slots (I found a way clientside tho, but that wont help in this case), so you have to set it manually. Also, the number of reserved slots isnt additional slots, the formula is publicslots = maxslots-reservedslots.
Could it also be so that it actually were more people on the server than the server browser argued? I noticed such a bug at times.
If any other modder know a way to kick people in a good way (maybe even with a custom msg in the box that always show up as "disconnected" (or whatever it says)), I would love to know it ;).
I've been using the following code now for a little while and it seems to work:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Function to get number of players on server
local function numPlayers()
local numPlayers = #GetGamerules().playerList
return numPlayers
end<!--c2--></div><!--ec2-->
Server.DisconnectClient does what you want
I never been a fan of kicking players to let others in, that promotes elitism to much. So I designed this solution to be more like, your server have x slots, but if you really want to play, you can join x 'nonexisting' slots.
Also, thanks for the help in finding functions, I will try them.
Altough the way of getting numplayers you use, seem to be very similar to the way I use. I checked gamerules, and it lack the playerlist variable entirely, altough ns2gamerules.lua got it (which is what you get with getgamerules). The playerlist is set with this code:
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function NS2Gamerules:UpdatePlayerList()
self.playerList = {}
table.adduniquetable(self.worldTeam:GetPlayers(), self.playerList)
table.adduniquetable(self.team1:GetPlayers(), self.playerList)
table.adduniquetable(self.team2:GetPlayers(), self.playerList)
table.adduniquetable(self.spectatorTeam:GetPlayers(), self.playerList)
end<!--c2--></div><!--ec2-->
Which as you see is pretty much exactly the same thing I do, unless someone somehow is in several teams at once.
I will use your function though, it is better as it has safety check for ppl in 2 teams.
Bugtesting time! :P
EDIT:
Ok, the client kicking function is deffinitelly better, and then numplayer code works atleast as well as my old code (suprise suprise! ;D).
Updating the code in the first post now.
EDIT2:
Also, does anyone know a good way from server to show text on players screen? Broadcast is small text in left corner, which evaporates very fast :P.
Yeah, I think this is the critical point, I think at some point due to some bug, a player may be in two teams at a time. Im not 100% sure if this was the bug anyways, but it has worked so far.
Will try the new kicking method, thanks :)
edit: You can show a tooltip with AddToolTipOnce (or something like that). But such a tooltip also disappears pretty fast so I dont really know whats better...
Was thinking more like printing in clients console as a server (and keep the current broadcast aswell).
Figure out how to hide the res slot asap and kicking people who are enjoying their game is a terrible solution.
Second, try joining a server which is not near-full, but instead one that has a few free slots left. Also, if the server stops using this mod, chances are they will also have less slots available (like armorychambers was a 10 slot, but is 11 now, where 1 is a reserved slot (which means there are still 10 slots for non-VIP's).