Just add Lua

One of my requirements for the Altair project is to make it as moddable as possible. To that end, I decided that the base game itself should be implemented as a mod in order to ensure that modders would have as much support as I could possibly give them. My first step down that road was to embed a Lua interpreter and create abilities in Lua rather than hard coding them in C#. This effectively makes abilities data and the data is executable code which makes it incredibly powerful.

Below is my first draft of what an ability script written in Lua might look like. This script does technically run at the moment but ends up spitting out console messages when making the calls to the game object rather than actually doing anything useful.

Note I have never written anything in Lua before so if I’m doing something very wrong, please feel free to let me know in the comments.

--[[
init is called when the ability is first initialized and should setup the provided
ability object so that the UI knows how to make it available to the player

parameters:
	ability		the ability's setup object that should be modified
--]]
function init (ability)
	ability.Id = "base.selfdestruct"
	ability.Name = "Self Destruct"			-- name to display for this ability
	ability.Icon = "self_destruct.png"		-- the image to use as the icon for this ability
	ability.Description = "Destroy the selected ship and damage all surrounding ships."
											-- description to be displayed for this ability
	ability.Source = "PlayerShip"			-- ability can be activated only from the current player's selected ship
	ability.Target = "PlayerShip"			-- target the current player's selected ship for this ability
	ability.PowerCost = 100					-- cost in power to activate this ability
	ability.Cooldown = 0					-- the number of turns before this ability can be activated again
end

--[[
activate is called when the ability is activated and is where the ability's effect should occur

parameters:
	game		object used to sense and affect game state
	source		the object that triggered this ability
	targets		a table of the target objects for this ability
--]]
function activate (game, source, targets)
	range = 100		-- range of the explosion
	damage = 100000	-- damage to apply to any ships in range of the explosion

	-- since this is a single target ability, use targets[1] to select the first target from the table
	ships = game.GetShipsInRange(source.Position.Position, range)

	if ship ~= nil then
		for i = 1, #ships do
			game.ApplyDamage(ships[i], damage)
		end
	end

	game.ApplyDamage(source, damage)
end

At game start, Altair parses the abilities folder for Lua files and executes the init function in each script which is responsible for setting up the ability in the engine. The ability is a dead simple object that exposes the properties set in the init function along with a reference to the script object itself so that I can execute it later.

So how do we execute Lua code in Unity? I’m glad you asked! I stumbled across the MoonSharp project a while ago and decided to give it a shot for this implementation.

Calling in to the activate function in the Lua script looks like this:

public override void Execute() {
	var Abilities = AbilityLoader.GetAbilities();

	var ability = Abilities[AbilityId];
	var pGame = UserData.Create(GameField);
	var pSource = UserData.Create(new Ship {Id = "Ship1"});
	var ships = new List<Ship> {new Ship {Id = "Ship2"}, new Ship {Id = "Ship3"}};
	var pTargets = UserData.Create(ships);

	ability.Script.Call(ability.Script.Globals["activate"], pGame, pSource, pTargets);
}

This code is mostly just temporary while I’m testing out the integration but I’ll walk you through it anyway. The AbilityLoader is the code responsible for parsing the abilities and executing the init method but after that its this bad boy that calls the activate function in the script whenever the ability is activated.

As described in the comments of the Lua script, the parameters are; the GameField object which allows the script to sense and affect the game, the source ship that triggered this ability and finally the targets for this ability.

Just for the sake of completion, this is the LoadAbility method that’s doing the script initialization.

private Ability LoadAbility(string path) {
	var ability = new Ability();
	ability.Script = new Script();
	ability.Script.Options.DebugPrint = s => { Debug.Log(s); };
	UserData.RegisterType<Ability>();

	var scriptText = File.ReadAllText(path);
	ability.Script.DoString(scriptText);
	var pAbility = UserData.Create(ability);
	ability.Script.Call(ability.Script.Globals["init"], pAbility);

	return ability;
}

That guy there is calling the init function in the Lua script and passing it an Ability object that the Lua script populates and then I can store the whole thing for later.

That’s pretty much it. Integrating MoonSharp was pretty trivial.

This has of course brought a large portion of the rest of my codebase in to question as my game field isn’t actually smart enough to make this work properly yet, so I’m looking at paying down some technical debt in the not too distant future to get this all working the way I’m envisioning it should.

Overall I’m quite pleased with this approach although I am a little concerned that this is going to cause me grief with some platforms that demand AOT compilation, but one problem at a time right? :)

The Input Manager

ship
SHip/Submit
verb
past tense: shipped; past participle: shipped
1. transport (goods or people) on a ship.
“the wounded soldiers were shipped home”
2. (of a boat) take in (water) over the side.
3. zip up a build and save it on a server without sharing the link with anyone.

I just shipped version 0.1.8-alpha of Altair as per the 3rd definition above. I think it’s important to clearly define for myself what it means to actually ship a version of Altair. At some point I’ll start sharing this with people. IPromise toShare = new Promise();

What’s new?

  • Input manager to control which input contexts get a chance to process input in any given frame
  • Attack order which allows targeting a ship and a basic laser animation followed by an explosion!
  • Attacks are limited in range and will also account for obstructions. Example: the right-most ship targets the left-most ship and fires but hits the middle ship because.. wrong place wrong time.
    0.1.8-alpha_setup

The most interesting challenge with this release has been the implementation of the input manager to enable more control over user input.

The original input handling was trivial as it relied solely on the built-in OnMouseUpAsButton method baked in to every GameObject. This worked great as long as I was only concerned with a single input action which was Select Ship. The Move Ship action was baked in to the movement arc view which worked great because the select ship code only kicked in to effect if you were clicking on a ship, not empty space.

Of course to implement combat, I needed to be able to select a ship and then select its target without losing the current ship selection. I had a pretty good idea as to how to pull this off but went looking for examples anyway.

It is my hope that one day a post on this blog will prove to be as useful to someone as this post was useful to me.

My input manager is quite a bit simpler than the above linked implementation but it certainly confirmed that I was on the right track.

The input manager in Altair is implemented with a couple of pretty trivial interfaces:

public interface IInputManager {
    void Update();
    void Push(IInputContext context);
    IList<IInputContext> Peek();
    void Remove(IInputContext context);
}

public interface IInputContext {
    Handled Update();
    ContextPriority Priority { get; set; }
}

It’s basically a wrapper for a list masquerading as a stack. On each frame, InputManager.Update() is called which simply iterates over the highest priority input contexts that it knows about and lets them do what they need to do.

There are a couple of neat things in this implementation that I want to call out such as the ContextPriority. I decided that I needed to be able to have more explicit control over where an input context would live in the stack rather than just pushing it on to the top. The ContextPriority is an enum that I’m casting to an integer value which is used to order priorities any time one is added to the queue. If no priority is provided, then it is given the current highest priority value +1 which is basically intended to allow me to make overlays or something similar later.

A case that I had to account for with the ContextPriority bit in play was the potential for duplicate priorities. I solved this by allowing a list of input contexts to exist at any priority. The actual declaration for that data storage looks like this:

SortedList<int, List<IInputContext>>

The int is the priority which then keys in to a list of input context objects. All input context objects within a given priority are treated as equal and are given the opportunity to handle input regardless of whether a sibling input context has returned a true value for Handled.

That’s another neat thing that I don’t see enough of when people are writing code in C#. C# allows us to create rich data types that make things easier and safer to use. Take a look at the return value of IInputContext.Update() for example. It is of type Handled. It could just as easily be a boolean as that’s basically how it’s used, but check out this implementation:

public struct Handled {
    public bool IsHandled { get; private set; }
    public Handled(bool isHandled) {
        IsHandled = isHandled;
    }
    public static implicit operator Handled(bool isHandled) {
        return new Handled(isHandled);
    }
    public static implicit operator bool (Handled handled) {
        return handled.IsHandled;
    }
}

That little bit of code lets me have a nice type-safe object that happily converts itself to a bool as required to support my common use case. I use this technique in several places throughout the codebase so far. A demonstrably more useful place for this technique is the ShipId type which is similair but implicitly converts to a string. It makes it super easy to find any location that requires the id of a ship and to ensure that those methods get what they need.

So, what’s next?

I think the next push is going to be to implement a game lobby where players will actually be able to join a game and begin with ship placement and follow that up with being able to play through a simple game of cat and mouse (movement and shooting at one another until somebody explodes). I might even go so far as to suggest that if I can pull that off, I might actually have a game on my hands!

Alright that’s it for now. Let’s wrap this up with a quick clip of Altair 0.1.8-alpha in action!

Ship placement and movement, now with friends!

It was certainly longer than expected, but I successfully reached the 0.1.7-alpha milestone which officially adds preliminary multiplayer turn based ship placement and movement.

So what does that look like?

client_server_0.1.7-alpha

Oh there’s also a shiny new space skybox. These are super easy (and fun!) to render yourself using Spacescape. And to think I almost bought a skybox from the asset store.. ha!

Much has been learned about many different things, I shall touch on a few.

First, I want to talk about the game model. Something I completely missed in my original design was the notion of maintaining a server state  independent of the client game state. This became evident really quickly in two ways; trying to run a game client in the same process as the game server and trying to have a network client submit the turn ahead of the game host submitting the turn. Basically in both cases, the preview functionality was impossible to manage properly as any preview would actually impact the server’s state and trying to reset to the round’s start state was much more difficult than it needed to be. This was resolved pretty easily by maintaining a completely separate server game state that is loaded into the game field only when the server actually needs to process a turn. Then the start state, player orders and end state are all pushed out to the connected clients to enable local playback of the turn results. At the moment this means each client is really just processing the turn again locally and maintaining their own local state, which in some weird situations might actually put a client out of sync with the server, but that sounds like a problem for future Chris!

Another issue that bit me pretty early on was the default UNet channel for network communications is a ReliableSequenced channel. This limits message sizes to the MTU of the network layer. This is typically somewhere around 1500 bytes but can vary. This caused problems in my early testing of the new serialization system. As you can imagine, serializing data objects into JSON to fire across the wire can get pretty verbose. A few turns going across the wire and I was hitting an error about large message sizes. Some googling showed a few similar issues which pointed me in the right direction. I simply changed the channel of my connection to be a ReliableFragmented channel and the problem went away! I’m sure it has created me a new problem, but for now that’s good enough. One thing to keep in mind here is that when you’re mucking about with network channels, make sure your client and server are using the same channel configurations. Trust me on this one, you’ll thank me for it later.

Finally I ran in to some interesting problems with the way my movement code was working. I had originally crafted a simple Coroutine to do the dirty work across multiple frames which saved me the hassle of trying to manage state in update methods. However, when trying to get the server process a turn, I discovered that the server was ending the turn immediately upon starting it and firing off the state back to the client without actually having moved anything. Well as it turns out, I was basically processing the turn orders synchronously without waiting for the asynchronous movement to actually finish first! This turned out to be a pretty easy fix. I updated to the latest version of strangeioc which I knew had support for promises. Documentation on strangeioc promises is a little light at the moment so I’ll leave the link to the blog post about it here. Long story short, I was able to add promise support to my turn processor and it now looks like this:

public override void Execute() {
    Server.GameState.Process(GameField);
    Server.GameState.Add(Server.GameState.CurrentRound);
    Server.Send(Server.GameState.CurrentRound);
    Server.GameState.CurrentRound.Orders.Clear();
    Server.GameState.CurrentRound.StartState = new RoundState(Server.GameState.CurrentRound.EndState);

    Retain();
    Server.GameState.Process(GameField).Then(FinishUp);
}

private void FinishUp() {
    Server.Send(Server.GameState.Commit());
}

My movement coroutine now looks something like this:

private IPromise moveToPromise = new Promise();

private IEnumerator moveTo(Vector3 target) {
    var arc = new ArcPathSegment(View.transform.forward, View.transform.position, target, View.transform.up);
    float speed = View.Data.Speed;
    float p = 0f;
    float d = 0f;
    while (p < 1.0f) {
        d = speed * Time.deltaTime / arc.Length;
        p += d ;
        rigidBody.MovePosition(arc.GetPoint(p));
        View.transform.forward = arc.GetTangent(View.transform.position);
        View.Data.Position.Position = transform.position;
        View.Data.Position.Rotation = transform.rotation.eulerAngles;
       yield return null;
    }
    rigidBody.MovePosition(arc.GetPoint(1.0f));
    View.Data.Position.Position = transform.position;
    View.Data.Position.Rotation = transform.rotation.eulerAngles;
    moveToPromise.Dispatch();
 }

In case you missed it, the magic is that call to moveToPromise.Dispatch() after the coroutine ends. This triggers the fulfillment of the promise acquired in the turn processor which then fires the FinishUp method once all asynchronous movement has completed.

I’ll be honest, I’ve had trouble wrapping my head around exactly where to use promises but this seems like a pretty good fit.

That about wraps up this post. I’m not entirely sure what my next goal is but I definitely want to get working on combat sooner rather than later. That would put me very close to an actual game that could actually start to be playtested! That would be pretty cool. :)

Anyhow, until next time..

My turn (part 2)

I’ve learned much since my last post working with Unity’s new networking layer. I’ve stumbled, I’ve fallen, I’ve picked myself back up only to have UNet push me back down again with its comically over-sized shoe.

this_is_unet

The fundamental problem I am facing is that Unity’s high level networking api (HLAPI for short) is designed to make a certain breed of game simpler. Those are any sort of real-time game where players are expected to see the same actions on-screen at the same time. This covers a not insignificant number of game types, but does not cover Altair.

Altair is a turn-based game where each player sees their own unique view of the game state and is free to manipulate it as they see fit to plan out their next move. This could mean they test out several orders and revert them before settling on a decision and submitting their turn for processing or they may issue orders to units that they do not control in an attempt to predict the outcome of a particular tactical maneuver.

Players are also given the opportunity to replay the results of the last round as they see fit. They can watch the action unfold from any angle over and over again to try and determine their opponent’s strategy before planning their next move.

Neither of these two requirements work terribly well with a networking model based around automatically updating the state of shared game objects across multiple players.

So my first hope was to free myself of the majority of networking complexities by leveraging the default NetworkManager where possible and dropping down to a lower level messaging API without having to worry about synchronizing game objects. This basically ends up utilizing Unity’s built in PlayerObject concept which makes things a little bit irritating but theoretically workable.

There are two problems that I ran in to with the baked in PlayerObject. The first is actually caused by my choice of underlying framework. I settled on strangeioc a while back as it promised a beautiful nirvana of event-driven and disconnected game code not unlike some enterprise applications I’ve worked with. The short version here is that strangeioc encourages you to structure your application in a certain way and it just doesn’t fit together nicely with the HLAPI in UNet. There is very little information to be found on this topic at the time of this writing but just know that you’re going to have a heck of a time trying to bring these two things together.

Assuming I could live with the hacks I had to do to get UNet and strange working together, the biggest problem is actually with the PlayerObject itself as it enforces a server authoritative model which comes with a bunch of intentional limitations to funnel communications to the server exclusively through the PlayerObject as Command and ClientRpc calls. I have been playing with this idea for over a week and I thought I was getting somewhere until I tried to actually send data to the server.

As I mentioned in my previous post, Unity only lets you serialize a relatively small subset of what I actually need to be able to serialize. The biggest gotcha is the inability to serialize complex classes of any kind. This was a problem and so I turned to using the built in .NET serializers to instead generate a byte array or some other primitive representation to fire across the wire. That’s when I ran in to the next problem:

SerializationException: Type UnityEngine.Vector3 is not marked as Serializable.

So much for that then. Without having those basic Unity types marked as Serializable, I would need to either create some sort of surrogate data types for serialization or write custom serialization methods all over the place. Not terribly appealing.

For my next and final attempt at this, I decided to go to the Unity community and find a serializer that would actually do what I needed it to do. I toyed around with a couple of options including protobuf but ultimately settled on fullserializer which happens to be supported on every target platform that Unity can publish to; how convenient!

I would love to have a happy ending to be able to wrap this post up with but the reality is that I’m still chipping away at turning this in to a working solution.

My current build does have designated GameServer and GameClient objects that both play nicely with strangeioc and I am able to serialize objects into messages and have handlers process them appropriately on both the server and the client.

There is however still a whole lot of housekeeping that needs to be done like linking a network connection to a player representation and plugging it in to the actual game model.

I suppose I needed something to write about next week anyway..

My turn (part 1)

Unity has all sorts of automatic support for networking.. as long as you’re looking to build a real-time game where your objects automatically synchronize across all your clients from a single controlling host.

Things get a bit more difficult if you’re trying to make a turn based game.

Altair is such a game. Players submit turns by clicking the end turn button which queues up the commands until everyone has completed their turn. Once all players have completed their turn, a signal gets dispatched to go along and collect all the commands and drop them in to the turn processor.

This part is actually pretty elegant. It leverages strangeioc’s signalling mechanism to fire off an EndTurnSignal  of which there could be any number of listeners. The signal is dispatched synchronously to all listeners and each listener is passed a list object to populate with whatever player commands they wish. Once every observer has had its turn to do what it needs to do, an EndTurnCommand is dispatched which takes the command list, fires it into the bound turn processor and actually calls the Process method accordingly.

So this works great in a single player setup (and is wonderfully disconnected I might add), but things start to break down when we want to have this work over the network.

Each player actually has their own unique view of the game world. Why? Because each player is going to have the ability to scrub back and forth over the turn history so they can see what happened from any angle at their own pace.

This presents an interesting challenge as we are no longer able to leverage most of Unity’s automatic synchronization magicks. If we did, everyone would see what the host sees and that’s not the point at all.

selected_ship_0.1.7-alpha
selected ship + movement ui overlay

So what do we do?

Honestly, I’m still sorting it out. I’m working with the idea of simply serializing the command list across the network as required but Unity won’t serialize any complex classes. The list of supported types pulled from the docs is:

  • basic types (byte, int, float, string, UInt64, etc)
  • arrays of basic types
  • structs containing allowable types
  • built-in unity math types (Vector3, Quaternion, etc)
  • NetworkIdentity
  • NetworkInstanceId
  • NetworkHash128
  • GameObject with a NetworkIdentity component attached

Note how it doesn’t even accept classes of any kind? Structs only! No lists either, arrays only!

My current approach is to create a custom network view of each command in struct form to enable serialization across the network pipe. I think this is a good thing as it will actually force me to think about what really needs to make that jump. It is however going to require a bit of extra work as I am probably going to have to implement my own id system (of which I’ve already mostly done anyway) in order to keep track of the game state.

Wish me luck!

Tune in next time for part 2 of this post.. In which I will hopefully have an actual working multiplayer turn processor!

How strange..

When I first started to look at strangeioc, I felt a little bit like I was headed down the all too familiar path of learning something new just because it’s there. It’s not like I have a huge history of successfully completing.. well.. anything. So it was entirely possible that altair was doomed..

It turns out however that strangeioc is not only really cool, but highly functional as well.

Strange (see how I dropped the ioc? It’s because we’re friends now.) lets me code in a style that I am more accustomed to seeing in enterprise grade business applications rather than game development; the MVC development pattern.

The beauty here is just how natural a fit this actually is. My views get to be whatever they need to be to get the pixels onto the screen and intercept user input. Then they get nicely translated into the appropriate events by way of a mediator object before getting routed to an appropriate controller/command to actually make things happen. And all of this happens by way of an easy to follow binding process.

If you’re working in Unity and haven’t given strangeioc a try (The ioc is back. Things got weird. People were beginning to talk.), I recommend you check it out.

My latest efforts have seen me refactoring (for some very loose definition of refactoring) my movement arc into its own game object so that I can easily put it on.. anything.

I’ve bumped into some interesting scenarios with using the default object picking functionality built in to Unity. Specifically, there’s a beautiful little method called OnMouseUpAsButton which fires off the appropriate events on an object that you’ve clicked on. It however relies under the hood on simple raycasting which has the undesired effect of having objects get in the way of my mouse clicking.

movement_arc_collision_box

In this image you’ll note the green box is the clickable region used by the movement arc. There’s some logic in there to ensure that it only actually uses clicks that fall within the designated movement space but that bounding box is a Unity collider. This is a really easy way to leverage the built in click functionality. However, note the asteroid in the lower right corner and how it overlaps with our bounding box. Its collision mesh actually ends up coming first along the y-axis so it intercepts clicks in that corner.

How annoying..

The quick fix is quite simply to move the asteroid on to the Ignore Raycast layer. This layer is ignored when doing the built in mouse click raycasting. This of course has a caveat, you can’t put your object on another more meaningful layer. I suppose you could use tags to help organize things but the physics system leverages layers so I’m certainly losing something.

This solution seems good enough for me for now as I don’t intend to ever have a player click on an asteroid. We’ll save that for my next game, asteroid clicker.

Also, blogger was terrible so I’ve migrated to WordPress in the hopes that it is slightly less terrible. Let’s see how it goes!