![]() |
|
Dedicated Server Discuss technical issues related to hosting your own servers. |
![]() |
|
Thread Tools | Display Modes |
#1
|
|||
|
|||
![]()
Part 1 - Introduction + downloads
Part 2 - Tutorials Part 3 - Example scripts Part 3 - Modifying the source + compiling Part 5 - Scripting Reference ::::::: INTRODUCTION ::::::: Alti Server Tools, or AST, is a scriptable altitude server log parser thing ^^ - Script the altitude server software in LUA ^.^ - Track all server events and respond with commands - Write custom commands with no need to add commands to the conifg - Tracks player and server info (nickname, vID's + IP's, in game stats, etc..) - very little overhead - Share scripts with other people easily, drag it into a folder and you're good to go Download: Contains windows(x86) binaries and source code Download [see below for compiling instructions] Using it: !! Important:: when running in Linux make sure the user you are running it as has read and write access to the alti directory and all sub-directories and files !! 1. Make sure you have a working server setup already, 2. Extract the zip, put the content of the 'Altitude' folder into your altitude directory and run altiServerTools.exe When you launch it without the server running, it'll run the launcher automatically 3. (Optional) write some scripts, and share them here! ![]() Any scripts put in the 'scripts' folder will be loaded when the application starts, or when they're reloaded manually using the /reloadScripts command in game, scripts starting with an underscore will not be loaded. There are a couple of short exammple scripts in the /scripts/examples folder, just move them to the main /scripts/ folder if you want to use them ![]() How does it work? Essentially it listens to events in the json log, and when it finds one it calls a function in all of the loaded lua scripts which contain that specific function. It also registers c++ functions with the lua script containers so you can interact with the server from within the script. If you don't know what lua it, it's a very simple, intuitive scripting language with bindings for c++ Once you know the basics writing simple scripts is easy ^^ All of the server commands which I'm aware of are available as functions from within the lua scripts, and the app also tracks the player info, so you can get a players name, kills, plane, perks and all the other info you could want from the player number simply by calling it like this: playerName = player_info (playerNo, "nickname"); Using the magic of, well, magic, the function returns the correct variable regardless of the data type (if you ask for a string, a name or vapor ID for example, it will return a string), lua's just cool like that, it's not fussy about data types. I think the best way to describe it is with examples, so see the tutorials and examples below There's full documentation for all the info available and all the other events and functions at the bottom. ___ RANDOM (MEANINGLESS) DISCLAMER: This is a WIP, it is subject to change and not guaranteed to bug free, work as expected, and not to set your house on fire. If any of these symptoms affect you in any way, I will attempt to help you, but take no responsibility for possible loss of data resulting from a bug or misuse of this software. ___ Last edited by LewisH; 07-01-2014 at 09:51 PM. |
#2
|
|||
|
|||
![]()
::::::: TUTORIALS :::::::
Before you get going, you might want to read this: http://luatut.com/crash_course.html ^.^ These tutorials are made to be completely beginner friendly, if you already know some lua and understand the basic concept behind AST you could just skip to the examples section. That being said, I don't often write tutorials, so it there's anything I haven't explained don't hesitate to ask Here it goes! The basics: I find the best way to learn is by just doing something, so let's start with that ![]() 1. Open a text editor 2. Type this: Code:
function playerJoin( port, playerID) name = player_info(playerID,"nickname") whisper(name, "Hello " .. name ) end 4. Run AltiServerTools, and join your server If all goes well, you should see a server message welcoming you to the server This is the main principle behind AST. It's kind of like the 'signals and slots' mechanism used in Qt programing. The server saves an 'event' to the log file, AST reads the log file, then calls functions in all scripts which want to know about the event. function playerJoin(port,playerID) The playerJoin function above is an example of an event. This function will be run every time a player joins your server, similarly if you had a 'goal' function, every time someone scores a goal the function will run. the parts in brackets are the function's arguments (or parameters). When that function is called, the thing that called it (in this case AST) sends it some information in the form of variables. Here we have two variables. The first, 'port' is the port of the server where the event took place, the second, 'playerID', is the number of the player who joined. All functions called by AST start with a 'port' argument, allowing you to distinguish between events on different servers. This is followed by information relevant to the event, for example the 'spawn' function would look like this: function spawn (port, perkBlue, perkGreen, perkRed, plane, player, skin, team) The names of the variables are not important, what is important is the position of them. The values shown the reference section are in the order you need. This allows you to respond to the event, sending commands to the server. name = player_info(playerID,"nickname") All of the events give the player as a number, this is just the way altitude works. From this number you can get all of the players information by using the player_info function as shown above. The first argument is the number of the player, in this case the player who has joined, the second argument is what you want to know about them, this could be anything from their vaporID or IP, to their kills, goals, perks, and even what power-up they're holding, here we just want to know their nickname so we create a variable called 'name', and set it to the "nickname" of the "player" who has joined ![]() whisper(name, "Hello " .. name ) There's no point knowing the name of the player if we're not going to do anything with it ^.^ The commands are similar to the server commands, in fact a lot of the commands available simply send commands to the server directly. Using this, you can change the team of player, change the map, kick, ban and drop players, stop and start tournaments, and communicate with the players using commands like whisper and serverMessage For a list of events and commands see the reference section below. By default the commands will be sent to the same server the event came from, you can use the function set_port([port]) to send commands to different servers. the ".." is lua's way of connecting(or concating to be technical) strings, for example "Hello ".."world!" would produce the string "Helo world!". Here we are using this to connect a variable to a constant string. end Unfortunately all good things must come to an end. This line represents the end of the function, and without it your script will not work ![]() note: a script file will be ignored if it's name begins with an underscore '_' ( e.g. "_example_script.lua") lua tutorials: To write more complex scripts, you'll need to learn some lua, a good tutorial is available here: http://www.phailed.me/2011/02/learn-...rd-way-1/#desc Too slow for your liking? try this: http://luatut.com/crash_course.html For a more in-depth approach, the manual's always good ![]() http://www.lua.org/manual/5.2/manual.html Scripting custom commands: Adding a custom command is simple. Format: Code:
add_cmd ( [command] , [vote threshold]) set_cmd_args ( [command] , [arg 1 type] , ...) set_cmd_descriptions ( [command] , [arg 1 description] , ...) function [command] (port, playerID, arg1, arg2, ...) --do stuff end Code:
add_cmd ( "kick_loops" , 100) set_cmd_args ( "string" ) set_cmd_descriptions ( "reason" ) function kick_loops(port, playerID, reason) for i = 0, serverInfo("max_players"),1 do if player_info (i,"plane") == "loopy" then ban(player_info (i,"nickname"), 2, "minutes", reason) end end end note: Commands are shared across script files, they can be set in one and the function be in another, if the same command is set twice, it will only be added once, and if you have the same function in two files, both functions will be called. The ”add_cmd” function adds the specified command to the custom commands file (if it's not already there), then makes sure whenever someone uses the command the associated function is called. The first argument is the name of the command, the second is the vote threshold: [from http://altitudegame.com/forums/showthread.php?t=4826] 0 = all users can invoke the command directly 1-99 = non-admins can invoke the command only through a vote (to pass more than X% must vote yes), 100 = only admins can invoke the command If the script was loaded by the reload_scripts command and a new command has been added, the server will need to be restarted for the changes to take effect The set_cmd_args function sets the arguments of a command, this can be either "int", "string", "player", "map" or an array of possible values in literal string form, e.g. Code:
set_cmd_args( [[ ["option1","option2"] ]] ) the set_cmd_descriptions command sets the relevant descriptions for the arguments defined above. After setting these parameters, you write the actual function, the thing that runs when someone uses the command ^.^ All custom_command functions, like other functions, have a port argument first, followed by a playerNumber argument equal to the number of the player who called the command, then the arguments specified by the set_cmd_args function. The function name must be the same as the command name, for example the function for a command "/kickme [reason]" could look something like this: Code:
function kickme(port, playerID, server_msg) server_message(player_info(player,”nickname”)..” has been perma-banned because “..server_msg) drop(player_info(player,”nickname”)) end Last edited by LewisH; 04-16-2013 at 09:06 PM. |
#3
|
|||
|
|||
![]()
::::::: EXAMPLE SCRIPTS :::::::
It's always easier to read scripts than it is to write them, hopefully reading a few of mine will give you a general idea of how AST works ![]() ^edit: I must have been tired when I wrote that, i meant it's easier to understand a scripting system by reading scripts as opposed to writing them ![]() These scripts can be found in the scripts/examples directory. --is_admin command, checks if a player is an admin: Code:
add_cmd("is_admin",0) set_cmd_args("is_admin","player") set_cmd_descriptions("is_admin","player") function is_admin(port,playerID,test_player) player_req_n = player_info(playerID,"nickname") player_test_n = player_info(test_player,"nickname") if player_info(test_player,"is_admin") then whisper(player_req_n,player_test_n.." is an admin") else whisper(player_req_n,player_test_n.." is not an admin") end end -- Will reset on scripts being reloaded --Usage: /prevent_spawn [plane name] -- /allow_spawn [plane name] Code:
loop_canspawn = true bomber_canspawn = true whale_canspawn = true bip_canspawn = true randa_canspawn = true add_cmd ( "prevent_spawn", 100) set_cmd_args ( "prevent_spawn", [[ ["loop","bomber","whale","bip","randa"] ]]) set_cmd_descriptions ( "prevent_spawn", "plane type") add_cmd ( "allow_spawn", 100) set_cmd_args ( "allow_spawn", [[ ["loop","bomber","whale","bip","randa"] ]]) set_cmd_descriptions ( "allow_spawn", "plane type") function allow_spawn(port,playerID,plane_type) serverMessage("A server admin has allowed "..plane_type.."s to spawn ^^") if plane_type == "loop" then loop_canspawn = true elseif plane_type == "bomber" then bomber_canspawn = true elseif plane_type == "whale" then whale_canspawn = true elseif plane_type == "bip" then bip_canspawn = true elseif plane_type == "randa" then randa_canspawn = true end end function prevent_spawn(port,playerID,plane_type) serverMessage("A server admin has prevented "..plane_type.."s from spawning :o") if plane_type == "loop" then loop_canspawn = false elseif plane_type == "bomber" then bomber_canspawn = false elseif plane_type == "whale" then whale_canspawn = false elseif plane_type == "bip" then bip_canspawn = false elseif plane_type == "randa" then randa_canspawn = false end end function spec_prevent_spawn(playerID,plane_name) p = player_info(playerID,"nickname") whisper(p, "Sorry, no "..plane_name.."s allowed") assignTeam(p,-1) end function spawn(port,pB,pG,pR,plane,playerID,skin,team) if (plane == "Loopy" and not loop_canspawn) then spec_prevent_spawn(playerID,"Loop") elseif (plane == "Bomber" and not bomber_canspawn) then spec_prevent_spawn(playerID,"Bomber") elseif (plane == "Explodet" and not whale_canspawn)then spec_prevent_spawn(playerID,"Explodet") elseif (plane == "Biplane" and not bip_canspawn) then spec_prevent_spawn(playerID,"Biplane") elseif (plane == "Miranda" and not randa_canspawn) then spec_prevent_spawn(playerID,"Miranda") end end that's it for now, more to come soon ![]() Last edited by LewisH; 04-16-2013 at 08:28 PM. |
#4
|
|||
|
|||
![]()
::::::: SOURCE CODE STUFF :::::::
Compiling: To compile you'll need the boost libraries and the lua c++ libraries. Linux: 1. If you've installed them to a non standard directory, Modify the makefile to point to the directories of your libraries and includes for lua and boost. 3. setup your compiler ad compiler flags in the makefile 2. Navigate to the source folder in a terminal window, and use the make command Visual studio, or another IDE: 1. Add all the source nad header files to a new project. 2. Go to the project settings and add the include + library directories for lua and boost 3. add the lua library to the linker settings. Note: I'm blindly assuming here that very few people wish to compile this, and those that do will have prior experience with c++. If I'm being completely ignorant here and this is not the case, please tell me and I’ll try my best to write a full compiling guide for Linux and windows, and I'll do my best with mac too ![]() Notes about the source: Everything is split up a lot. There may look like a lot of files, but some of the sources are less than a hundred lines long. Essentially at first I had the entire source code in one 5000 line c++ file, and it was too confusing for me so I split it up into what I though was a logical format and cut out what wasn't needed. There's not actually that much stuff there so it shouldn't be hard to make small adjustments if you want to ![]() I've tried to document it as well as I can, but some things are still messy and not as obvious as they should be, also some functions are not exception safe. They generally shouldn't completely crash the program, but if given incorrect parameters might mess up some things. All the error checking is focused on the lua / c++ integration, and to be honest internal exception safety is something I don't have the time (or skills) to do properly, so I'm not going to try. That being said, none of this should affect you unless you try major modifications ^.^ All of the functions which are registered to the lua scripts are stored in the lua_functions.h header file and only included in lua.cpp, basically because static lua functions can only be declared once. This is the best solution I could find (due to the functions being static) without bloating the lua.cpp file, or going beyond my depth programing knowledge wise. ::::::: REFERENCE ::::::: ::: Script function calls + parameters ::: Code:
note: All functions start with a port parameter format: function [function name] ( port, [other parameters] ) --do stuff end e.g. function chat (port, message, player, server) print("psst, they're talking! >_>") end Function Name: | parameters: ==================================================================================================== assist | playerID - victim - xp chat | message - playerID - server clientAdd | ip - nickname - playerID - vaporId clientNicknameChange | ip - newNickname - oldNickname - playerID - vaporId clientRemove | ip - nickname - playerID - reason - vaporId consoleCommandExecute | arguments - command - group - source customCommand | command demolitionChargeDefused | playerID demolitionChargePlanted | playerID goal | assister - player - xp kill | multi - playerID - source - streak - victim - xp mapChange | map - mode mapLoading | map pingSummary | pingByPlayer powerupAutoUse | playerID - positionX - positionY - powerup powerupDefuse | playerID - positionX - positionY - powerup - xp powerupPickup | playerID - positionX - positionY - powerup powerupUse | playerID - positionX - positionY - powerup - velocityX - velocityY serverHitch | changedMap - duration serverInit | maxPlayerCount - name serverShutdown | serverStart | sessionStart | NOTE: port=-1 - date spawn | perkBlue - perkGreen - perkRed - plane - playerID - skin - team structureDamage | playerID - target - xp structureDestroy | playerID - target - xp teamChange | playerID - team tournamentRoundEnd | losers - result - winners tournamentStart | team0 - team1 tournamentStop | updatePrepareRestart | ::::::::::::::::: lua Commands :::::::::::::::::: Code:
misc: reloadScripts() | reloads the scripts set_port( [port] ) | sets the port for next commands server commands: Usage: [command] ([values]) e.g. if bluePerk == "Reverse Thrust" then ban ( ".^-^.", 5, "years", "STOP REVING ON LADDER!!1!!1!") COMMAND: |SERVER EQUIVALENT: |VALUES: ==================================================================================================== addBan |addBan | ip/vID - duration - unit - reason asignTeam |assignTeam | nickname - team balanceTeams |balanceTeams | ban |ban | nickname - duration - unit - reason testPlaneScale |testPlaneScale | value testViewScale |testCameraViewScale | value serverMessage |serverMessage | message whisper |serverWhisper | nickname - message removeBan |removeBan | ip/vID nextMap |nextMap | kick |kick | nickname drop |drop | nickname startTournament |startTournament | stopTournament |stopTournament | changeServer |serverRequestPlayerChangeServer | nickname - serverIP:port - password Player Infomation command: player_info( [player number] , [value] ) OR pinfo ( [player number] , [value] ) value = one of the following (as a string) exist team plane redPerk bluePerk greenPerk skin nickname vapourID ip powerup all_kills all_deaths all_assists all_structure_damage all_goals kills deaths assists structure_damage goals streak ping is_admin is_bot server infomation command: server_info( [value] ) value = one of the following (as a string) current_players current_map max_players name Custom commands: Code:
add_cmd ( [command name] , [vote threashold]) // 0 = anyone can use // 1-99 = anyone can vote, value = vote pass % // 100 = only admins can use set_cmd_args ( [command name] , [arg1 type] , ... ) set_cmd_descriptions ( [command name] , [arg1 description , ... ) See tutorials section for an example Last edited by LewisH; 04-16-2013 at 08:23 PM. Reason: formating |
#5
|
|||
|
|||
![]()
I like that list of references.
Code:
powerupAutoUse, powerupDefuse, powerupPickup, powerupUse Player has powerup x use x off the map give player y powerup use y where I wanted it in map give player another powerup x replacement or give hp powerup as a reward for first time that player reached x location. or give hp powerup only to those with enough bars of xp I'm suddenly sad as I realize these are trigger references... not "go cause this" references. It was fun to dream. I'm seeing ambiguous points on some functions for player references. Is the provided or wanted information for player the player's number, the player's nickname, or perhaps a player object? Or is every point that something says player expecting a nickname? If player is always nickname, the pinfo'ing for nickname is silly. OTOH, the example for spec_prevent_spawn suggest that player in that case isn't the nickname. Code:
function spawn(port,pB,pG,pR,plane,player,skin,team) if (pB == "Reverse Thrust") if (pinfo(player, "is_admin")) chat("I'm telling Krawz on you!", pinfo(player, "nickname"), port) else kick(pinfo(player, "nickname")) end end end |
#6
|
|||
|
|||
![]()
Thanks for the feedback
![]() For the sake of familiarity and speed*, the player is given as a number just like it is recorded in the log, and the functions all take the player name as that is how the commands in-game work. I was considering changing the functions to use player numbers, but it would be slightly slower(again, string lookups vs vector indexing) Id be happy to change it if you wanted me to, I do see that it would make the scripts easier to read Oh, and the whisper function is available, bad copy + pasting on my part, i'll fix it now ![]() *the players are internally stored as a vector with the player number being the index, it would take much longer to search for the player name than to get the information form the index edit: the spec_prevent_spawn function creates a variable 'p' to hold the players name and uses that in the function calls. edit2: I just checked and the whisper function is in the reference, in the lua commands section just under the serverMessage command. Maybe you were looking in the wrong section? edit3: yeah, I wish it was possible to control pickups like that, there would be so many posiblities! ^.^ Last edited by LewisH; 04-16-2013 at 08:36 PM. Reason: typo |
#7
|
|||
|
|||
![]() Quote:
That's it. I was looking at script reference instead of server commands. |
#8
|
|||
|
|||
![]()
Yeah, looking back I see how using 'player' for everything is quite confusing
![]() I've changed the tutorial, example scripts and reference to use playerID where appropriate, this will be changed in the scripts in the example folder of the downloads with the next update. |
![]() |
|
|