User:Major Zeman
| http://forums.acidrainclan.com/uploads/MajorZeman/2006-04-26_152247_avatar_zeman.jpg | Major Zeman can be currently (= for about last 3 years) found at forums.acidrainclan.com. |
LUA Code fragments
During my first adventure into the world of LUA+ETPro, I ran into several problems. Here are a few functions/code fragments that I think are worth mentioning or could be useful to someone in future:
Get GUID
A simple function that gives you guid of the player in given slot. The function performs some checks and converts the guid to uppercase for exact matches.
Note: the function returns short version of guid and should NOT be used for authentication purposes!
-- gets user's guid
-- returns nil if not applicable to entity number
function getguid(targetID)
if (targetID == nil) or (targetID > tonumber(et.trap_Cvar_Get("sv_maxclients"))) or (not et.gentity_get(targetID,"inuse")) then
return nil
end
local userinfo = et.trap_GetUserinfo( targetID )
local guid = et.Info_ValueForKey( userinfo, "cl_guid" )
-- upcase for exact matches
guid = string.upper(guid)
return guid
end
Player's Position
This one was actually quite hard to make for me, involved some research in meanings of different items in entity info.
As long as the player is on one of the teams and alive, it's easy:
et.gentity_get(clientID, "origin")
However, when you count in spectators too, it becomes much more complex. Spectator's position isn't stored in "origin" but in "s.origin". And the trickiest part is when spectator choses to follow other player - his entity stops moving at all and his position is given by the position of player they're spectating. All together:
-- get client's position
function playerpos(clientID)
if et.gentity_get(clientID, "sess.sessionTeam") == 3 then
-- spectator
if et.gentity_get(clientID, "sess.spectatorState") == 2 then
-- spectator thats following someone
local spectatedID = et.gentity_get(clientID, "sess.spectatorClient")
return et.gentity_get(spectatedID, "origin")
else -- spectatorstate == 1
-- free spectator
return et.gentity_get(clientID, "s.origin")
end
else
-- regular player
return et.gentity_get(clientID, "origin")
end
end
Print message to console
This is actually a very useful little function. First, it lets you ignore the differences between printing text to a client's console and into server console, and second, it makes implementation of other functions easier.
-- print message to either client's console or (if clientID == nil) to server console
function printmsg(message, clientID)
if not message then
return
end
if clientID then
et.trap_SendServerCommand(clientID, "print \"".. message .."^7\n\"")
else
et.G_Print(message .."^7\n")
end
end
The way it works is it gets a message and eventually client number. If it got client number, it prints the message to that client, if it got null as client number, it prints to server console. Combined with the way LUA handles function call parameters, the use looks like this: printmsg("Hello", 2) prints "Hello" into console of client #2; printmsg("Hello") prints "Hello" into server console.
The second reason why I found it useful is, you can easily make functions that can be called through both server and client console and need to print some output - just give them clientID parameter, when calling it from server console put clientID = null and let the printmsg function handle it. Example of such function is the next one.
Player ID from name
This is an altered version of function posted on etpro forums by Im2good4u and Deus. I added some more parameter checks, error prints (using the above function, so it can be used both for server and client commands), made it accept slot numbers and partial name matches too.
-- returns clientslot for given name, or nil if no match found (accepts first (partial) match)
-- added color stripping
-- if clientID is filled, reports errors to clientID; else prints them to server console
function getPlayerId(name, clientID)
local i
-- if it's nil, return nil and throw error
if (name == "") then
printmsg("Not enough parameters.", clientID)
return nil
end
-- if it's a number, interpret as slot number
local clientnum = tonumber(name)
if clientnum then
if (clientnum <= tonumber(et.trap_Cvar_Get("sv_maxclients"))) and et.gentity_get(clientnum,"inuse") then
return clientnum
else
printmsg("Slot "..clientnum.." not in use.", clientID)
return nil
end
end
-- exact match first
for i=0,et.trap_Cvar_Get("sv_maxclients"),1 do
playeri = et.gentity_get(i,"pers.netname")
if playeri then
if et.Q_CleanStr( playeri ) == et.Q_CleanStr( name ) then
return i
end
end
end
-- partial match
for i=0,et.trap_Cvar_Get("sv_maxclients"),1 do
playeri = et.gentity_get(i,"pers.netname")
if playeri then
if (string.find(string.lower(et.Q_CleanStr( playeri )), string.lower(et.Q_CleanStr( name )), 1, true)) then
return i
end
end
end
printmsg("No name match found for '"..name.."'.", clientID)
return nil
end
Printing server LUA commans output to semiadmin console
Warning - it works, but it's a very dirty trick.
Unfortunately, there's no normal way to print output of semiadmin commands back to the semiadmin's console. I found a trick that allows you to get around this limitation at least for LUA custom commands. It's quite complex tho, and as I wrote, very nasty. Here it goes:
- first we need to declare a global variable (yeah trouble starts here) - it will actually store client number of last client who ran a /semiadmin or /sa command
-- a terrible hack to enable console output back to semiadmin lastcallerID = nil
- now we'll alter the printmsg function shown above a bit (yeah another advantage of that function heh)
-- print message to either client's console or (if clientID == nil) to server console
function printmsg(message, clientID)
if not message then
return
end
if clientID then
et.trap_SendServerCommand(clientID, "print \"".. message .."^7\n\"")
else
if lastcallerID then
et.trap_SendServerCommand(lastcallerID, "print \"".. message .."^7\n\"")
else
et.G_Print(message .."^7\n")
end
end
end
- fortunately, /sa and /semiadmin are ordinary commands and can be intercepted in CleintCommand trap:
function et_ClientCommand( clientNum, command )
-- lowercase
command = string.lower(command)
-- hack - console output to semiadmin
if (command == "sa") or (command == "semiadmin") then
lastcallerID = clientNum
end
...
end
- now we are basically set, all that remains is to clean up the mess after the party :S
- we need to erase the client ID from the variable so it won't affect someone else's calls; the second setting is after a command was executed already, the first one is for case no custom command was executed and we'll passing the command to ET
function et_ConsoleCommand()
if (string.lower(et.trap_Argv(0))=="...")
... -- all custom console commands go here
else
lastcallerID = nil -- hack to allow console output to semiadmin
return 0
end
lastcallerID = nil -- hack to allow console output to semiadmin
return 1
end
- and finally, we need to make sure semiadmin calling disallowed command will be handled correctly, too - for that I used console print trap as I haven't found any other way to detect that situation:
function et_Print(text)
if string.find(string.lower(et.Q_CleanStr( text )), ") invalid command [", 1, true) then
lastcallerID = nil
end
end
MZ's TJ mod
version: 1.5
released: 25.3.2007
[download zip | lua]
Description
Yet another /save /load mod - this one should be useful for learning and teaching how to trickjump, for pro TJ servers it's probably rather useless.
The mod is based on Kyoko's HSP_TJmod, with fragments of code (sometimes big fragments eh) from ==FF==im2good4u, Furion and robably also from ReyalP. Feengur's ZeN mod was a source of inspiration for me, too.
Key features (as found in Kyoko's TJmod)
- /save and /load - stores your possition on map for each team separately and allows you to teleport back
- /gotohell - server console command, gibs player
- /goto and /iwant - server console commands, teleport one person to another
- /goback - if the person was teleported using /iwant, this server console command moves them back to original location
New features (differences from original mod)
- some code cleanup and formating, parameter checks blah blah blah etc.
- /save2 and /load2 - second save slot. This one is common for both teams and you can /save2 and /load2 also as spectator
- easy jump skipping using /save2 as spectator (here you'll have to decide whether this mod is good or bad for your server)
- some more administrative commands: /rmsaves, /sdisable, /senable (inspired by ZeN)
- all commands allow partial name match as player identification in parameters (altered code from im2good4u)
- saves and /sdisable are persistent across player's reconnects (as long as the script is not reloaded by loading other map etc. the saves persist - the save slots are indexed by guid not player slot)
- /iwant and /goback print explanatory script to target
- /gotohell (and alias /gib) play a sound (again a bit of code from im2good4u) and print a message to everyone
- /disarm command strips a player off all weapons except syringes, knife, binocs and pliers (maybe some more but no shooting weapons) - either for current life or permanently (again altered im2good4u's code)
- /dist gives you distance from others - rather useless but fun command that I found has one practical use after all - you can see who specs you
- /realsay and /realvsay - sends message only to players close to you - this sounds pretty weird but it's useful if you want to explain a trickjump in one room and don't want to confuse those at other jumps
- /TJplayers server console command gives admins overview of info held about the connected players
- /TJhelp gives overview of client commands, /rcon TJhelp gives help for server console commands
- all server commands behave properly and print their output back to caller's console even if used through SemiAdmin command (found a workaround :)
Drawbacks
- no support for etadmin - never worked with that and don't know how it works
- tried to make users stop on load and failed just like all before me did :D
- the workaround to print output back to semiadmin is a bit messy and uses et_Print callback (possible server performance decrease)
- not good for pro TJ servers and seevers without active admins - you can really skip jumps easily and eventually end maps in no time
Thank you
Kyoko / Kotoko
im2good4u
Furion
ReyalP
Feengur
All the guys who wrote LUA API description and code examples here