Monthly Archive for March, 2009

Progress and trouble

I’ll start off with some amazing outside news. The highly anticipated map RP_EvoCity2 has just been released! For those who lived under a rock the past year, it’s the successor to RP_EvoCity1, a huge map featuring a wide range of areas including urban areas, nature and a lot more! Here’s some of my favorite screenshots:

Because I love this map and the work Sgt.Sgt has put in it, I’ve set up a mirror on my hosting subdomain!

SourceScript

Unfortunately there’s also some bad news. Development of SourceScript has pretty much stalled at the moment. This because me and Deco are facing a problem that has something to do with Source Engine linking problems. You can read more about it here, but until we find a solution, we’re stuck :( .

GM_Aperture

It’s probably a bad thing to say, but because I don’t have to dedicate all my time to SourceScript, I’ve got a lot more time to work on my mapping project. I haven’t done too much extra stuff yet, but I’ve added emergency lockdown doors :3:

Stay tuned for the storage floor and NPC cages!

A new developer and spare time!

Today Deco Da Man officially joined the development team of SourceScript. Unfortunately there isn’t much new things to show at the moment, but we’re writing a better way to handle entities and players now. After that basic system is done, adding methods like SetPos() and Nick() will be a cakewalk.

Something else I’ve been working on since this weekend in my spare time is the second map ever I’m going to release, GM_Aperture:



So, stay tuned for more updates :)

Players and hooks

Four days ago I introduced you to the idea of SourceScript, now I will tell you about the progress I’ve made the past few days.

Hooks

All the global hooks have been added, but not fully implemented yet, but I’ll tell you about that later. What do I mean by ‘global’ hooks? Well, when writing a server plugin you can create a game event ‘listener’ that tells the plugin when something happens, like a player connecting or the map changing. So basically Source’s built in hook system.

In the previous post I told you that SourceScript will run on any Source Engine game or mod, but obviously Team Fortress 2 has some events that doesn’t exist in Counter-Strike: Source and vice versa. So, global hooks are things like PlayerConnect and PlayerSpawn that exist in any multiplayer game.

Now, these are the added hooks:

  • Frame - Pretty much Garry’s Mod’s Think hook
  • MapInit - When the map has been loaded
  • MapShutdown - When the map is being changed or the server is shutdown
  • PlayerActivate - Right before a player spawns
  • ServerActivate - The map has completed loading
  • PlayerDisconnect - Duh
  • PlayerInServer - Also a prespawn hook
  • PlayerSettingsChanged - Someone’s nick or other setting changed
  • PlayerConnect - Doh
  • PlayerCommand - When a player runs a console command
  • GameEvent - This is for all game specific events like ‘hostage_follow’

The last hook is an interesting one. Soon that one will be divided into actual hooks, but for now you’ll have to deal with it in Lua:

function Event( eventname )
	Msg( "A game event has fired: " .. eventname .. "\n" )
end
hook.Add( "GameEvent", Event )

Players

Now, all the above hooks lack one simple thing. That’s passing a usable player object like seen in Garry’s Mod. I started off by just passing the entity index of players, but this started to get boring quickly.

Then I spend two days learning about metatables and calling them from Lua. Metatables are basically tables defining how other tables should be handled. If you want to know more about them, read this excellent tutorial by Deco Da Man. Yesterday I rewrote the PlayerActivate hook, so it uses the player object. The C++ code so far is really messy, because I just learned how to do it:

void hook_clientactivate(int entid)
{
	// First get the player table using the entity index
	lua_getfield( lua, LUA_GLOBALSINDEX, "Player" );	// Look up the table 'Player'
	lua_getfield( lua, -1, "GetByIndex" );				// Get the member 'GetByIndex'
	lua_remove( lua, -2 );								// Remove the table 'Player' from the stack
	lua_getfield( lua, LUA_GLOBALSINDEX, "Player" );	// Push the table 'Player' as the first argument
	lua_pushnumber( lua, entid );						// Push the entity index
	lua_call( lua, 2, 1 );								// Call the function Player:GetByIndex()
	lua_setfield( lua, LUA_GLOBALSINDEX, "tplayer" );

	// Now, call the hook with the achieved player table
	lua_getfield( lua, LUA_GLOBALSINDEX, "hook" );
	lua_getfield( lua, -1, "Call" );
	lua_pushstring( lua, "PlayerActivate" );
	lua_getfield( lua, LUA_GLOBALSINDEX, "tplayer" );
	lua_call( lua, 2, 0 );
}

I will probably rewrite it tomorrow, so it’ll work as easy as other hooks work now:

void hook_clientconnect(string nick, string ip)
{
	hook_start( "PlayerConnect" );
	lua_pushstring( lua, nick.c_str() );
	lua_pushstring( lua, ip.c_str() );
	hook_finish(2);
}

Eventually I wrote the following script (Which actually works :D ):

function Act( ply )
	Msg( "Player activated: " .. ply:Nick() .. "\n" )
end
hook.Add( "PlayerActivate", Act )

Doesn’t seem so exciting, but it’s pretty cool to see this in the console, knowing that it’s powered by a Lua script :3:

Stay tuned for more updates on the player object and don’t forget to let me know of your thoughts, since you’re a potential scripter who’s going to use this ;)

SourceScript

Introduction

SourceScript is the new project I’m working on at the moment. It’s easy to explain what it is and what it does. Ever wanted to write a gamemode in Lua for Counter-Strike: Source? With SourceScript you can. Ever wanted to write a Lua admin mod for Team Fortress 2? Also possible. The possibilities are endless if implemented well.

SourceScript scripting will be very similar to GMod Lua. Some of the similarities:

  • Both use Lua for scripting
  • They both work with hooks
  • Interaction with the Source Engine using entities and commands like RunConsoleCommand

Unfortunately there’s also some forced differences:

  • SourceScript is only serverside*
  • Therefore there’s hooks that aren’t in GMod and viceversa
  • Doesn’t use the Addon folder system (yet)

* We don’t want Lua aimbots, do we?

Current progress

Today I’ve first moved the hook managing to Lua, because it’s a bit easier and nicer. So I wrote some basic hook management code, which works fine so far:

--[[
	This file manages hooks and offers functions to register, call and remove them
]]--

Hooks = {}
hook = {}

function hook.Add( event, func )
	local newhook = {}
	newhook.event = event
	newhook.func = func

	if not table.HasValue(Hooks, newhook) then table.insert( Hooks, newhook ) end
end

function hook.Remove( event, func )
	for	i, v in pairs(Hooks) do
		if v.event == event and v.func == func then table.remove(Hooks, i) end
	end
end

function hook.Call( event, ... )
	for _, v in pairs(Hooks) do
		if v.event == event then v.func(...) end
	end
end

Then I figured I had no idea yet how to use tables in the Lua C API, so I used this shit for quick testing:

void hook_gameframe()
{
	script_runstring("hook.Call(\"GameFrame\")");
}

The above code is like turning the wheel with a stick in your car when taking a turn, so I quickly learnt how tables and accessing values in them work and came up with this: :3

void hook_gameframe()
{
	lua_getfield(lua, LUA_GLOBALSINDEX, "hook");
	lua_getfield(lua, -1, "Call");
	lua_pushstring(lua, "GameFrame");
	lua_call(lua, 1, 0);
}

I guess I could still fine tune it by writing a separate function for the first two lines, but oh well, I’m happy I got it right now.

Stay tuned!

New website design

Welcome to my new website, guys :3. A few things have changed in the switch. A few of the most exciting new features are:

  • There’s a blog on the homepage now
  • There’s now links on the right
  • And a project tracker!

Well that’s all I can think of at the moment, but the blog will really be a nice addition! I will post more information about current progress than the forums, and more detailed!

Enjoy!