Altitude Game: Forums  

Go Back   Altitude Game: Forums > Altitude Support > Dedicated Server
FAQ Community Calendar

Dedicated Server Discuss technical issues related to hosting your own servers.

Reply
 
Thread Tools Display Modes
  #1  
Old 04-16-2013, 05:50 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default AST | AltiServerTools

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! | See below for basic tutorials and how it all works |

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.
Reply With Quote
  #2  
Old 04-16-2013, 05:51 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default

::::::: 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
3. Save it in the scripts folder as hello.lua

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
example:

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.
Reply With Quote
  #3  
Old 04-16-2013, 05:51 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default

::::::: 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
-- Admin commands to temporally allow or prevent a plane type from spawning
-- 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.
Reply With Quote
  #4  
Old 04-16-2013, 05:52 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default

::::::: 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
Reply With Quote
  #5  
Old 04-16-2013, 07:29 PM
Aki1024 Aki1024 is offline
Senior Member
 
Join Date: Jul 2011
Location: Across from you at a chess table. Your play is?
Posts: 1,080
Default

I like that list of references.

Code:
powerupAutoUse, powerupDefuse, powerupPickup, powerupUse
Are you suggesting with these you can cause a powerup to get used at a position of choice? Can powerupUse just use anything, or does the asked player need to have the item?

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
spec_prevent_spawn has a reference to whisper. This doesn't appear in the reference. Is that a publicly available reference?
Reply With Quote
  #6  
Old 04-16-2013, 07:40 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default

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
Reply With Quote
  #7  
Old 04-16-2013, 08:12 PM
Aki1024 Aki1024 is offline
Senior Member
 
Join Date: Jul 2011
Location: Across from you at a chess table. Your play is?
Posts: 1,080
Default

Quote:
Originally Posted by LewisH View Post
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. [...]

Id be happy to change it if you wanted me to, I do see that it would make the scripts easier to read
At most, I'd be asking for naming conventions that are not ambiguous. playerID, playerVapor, playerNick or any variation that gives the same result.

Quote:
Originally Posted by LewisH View Post
edit2: I just checked and the whisper function is in the reference, in the lua commands section just under the serverWhisper command. Maybe you were looking in the wrong section?
That's it. I was looking at script reference instead of server commands.
Reply With Quote
  #8  
Old 04-16-2013, 08:35 PM
LewisH LewisH is offline
Senior Member
 
Join Date: Mar 2012
Location: Earth
Posts: 215
Default

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.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 06:02 AM.


Powered by vBulletin® Version 3.8.2
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
2008 Nimbly Games LLC