BGE Networking

Lots of changes recently…

  • AI behaviour is now easy (er) to implement, thanks to an extensible Behaviour Tree state graph.
  • Added animation support to pawns
  • Many bugs that existed in the deeper parts of the system, in replication, enum accessors and other fixes.
  • Added threaded updating of the matchmaker to avoid latency spikes in game

I’ve done quite a lot behind the scenes, which isn’t so apparent from the change log of my SVN. This has indicated that I’m doing too much per-revision. Instead, I should be revising even the smaller changes so that one can follow the development flow more easily.

  • Added support for voice chat, although fundamentally it is quite limited as one cannot use PushToTalk, nor mute players. This just needs to be supported in the PlayerController call.
  • Under the hood optimisations wherever possible.
  • Ensured replicables are all unregistered at the end of the game.
  • Added bytes replication support (bytes type).
  • Added support for longer strings and bytes (max_length argument)
  • Behaviour change for subclassing - previously defining a method in the base class does not prevent wrapping with a permission wrapper)
  • Moved collision callback handling to Actor class
  • Added extra methods/properties to Actor to facilitate suspended physics.
  • Reverted to state-driven animations again
  • Fixed role replication bug (caused by complain defaulting to True (as it is a muteable type))
  • Reverted complain default to False (nb Roles should not complain)
  • Fixed behaviour trees unvisited nodes (they weren’t exited)
  • Ability to have multiple signal listeners on one callback.
  • Renamed legacy naming (event) to signal in the signals module
  • Added ZombieWeapon example, modified flashcount system
  • Removed attack animation from behaviour code (animations belong to animations)
  • Added cleanup module to clean imports
  • Added explicit ValueError warning for non-existing GameObjects
  • Modified spacing on debug prints for behaviour tree
  • Run state cleanup on behaviour tree before update, rather than after (to fix
  • debug print)
  • Added decorator syntax for supplying instance data to RPC annotations
  • (@supply_data(annotation_name=[instance_name, instance_name, …]))
  • Ported control code of LegendController to behaviour tree
  • Moved camera data to camera from LegendController

The new library now requires pyaudio and opus for voice compression / capture.
I suspect that things may be missing / imports are relied upon that I’ve not bundled, so I will release a new patch and a new includes folder in the near future.

Nice! I’m kind of late to this, but I will definitely want to use it! Can’t wait to see the voice chat cleaned up!

Notable commits:

r254

  • Improved performance of changes
  • Renamed irrelevant_to_owner to relevant_to_owner, and inverted the default.

r250

  • Added replication_priority and replication_update_period attribute to
  • Replicables. This enables replication according to available bandwidth - most
  • important first, and to scale this depending on the time between updates. This
  • occurs only for attribute replication. See WIKI for more information
  • Added bandwidth monitoring - rough guess algorithm
  • Added packet filter that drops according the the guessed bandwidth. This occurs
  • only for attribute replication.
  • Moved certain Replicable attributes to class definition.
  • Optimisations / code cleanup
r249
  • Fixed replication info instances for AI and Players, including reference to the
    current pawn
  • Added set_name rpc for PlayerController
  • Fixed error where on_uninitialised was set as a client only function (preventing
    super() calls)

I could really use people to test this!

PyAudio + PyOpus
site-packages.zip (216 KB)

Blender Build:

Source Files:
https://code.google.com/p/py-auth-server/source/browse/#svn%2Ftrunk

Those PyAudio dependencies are Windows only. Do you have one built for OSX?

I don’t I’m afraid. I will look at getting those compiled

  • 269 27/12/2013 22:03 Added in-game support for replication of collision group and mask. Added behaviour to set collision mask of pawns as geometry (only) when killed
  • 268 27/12/2013 21:52 Updated gameloop for collision groups and masks Python API
  • 267 27/12/2013 21:09 Removed useless function in Physics. Fixed profiler for UI (used to be included in network profile times)
  • 266 27/12/2013 20:43 Testing update in network code
  • 265 27/12/2013 17:46 Added patch file for Blender (GIT)
  • 264 24/12/2013 01:47 Added overflow check for flash_count
  • 263 23/12/2013 23:54 Added notifications
  • 262 23/12/2013 02:08 Fixed bug when entities are deleted (not replicated to client). Added Notification system. Improved ManualTimer class. Fixed large bug with ReplicationRegister metaclass. Style changes
  • 261 21/12/2013 22:39 Updated docstrings. Cleaned StorageInterface and subclasses thereof
  • 260 16/12/2013 20:45 Python crashes using C bitfield, reverted to Pure Python
  • 259 15/12/2013 23:14 Removed pure-python bitfield
  • 258 15/12/2013 23:13 Compiled bitfield with Cython. Cleaned error if connection did not fully initialise before client disconnects
  • 256 13/12/2013 21:59 Updated docstrings. Removed some useless code

More changes!

Overall improvements, some serious. Latest patch included, this is my working copy (using GIT, MSVS 2013 professional, 32 bit)
I’ve updated the build linked above.

Removed post

291 14/02/2014
Cleaned weapon firing code

290 14/02/2014
Set target clock tick to include a default dejitter buffer of 100ms

289 14/02/2014
Fixed disparity between WorldInfo and Physics tick rates

288 14/02/2014
Remove delta_time from behaviour tree update Using single tick physics except for corrections (allows collisions between objects and players) Added clock synchronisation Added dumb dejitter buffer Fixed unittest cases and prevents the main function from exiting Blender Added buffered locks to support respect for dejittering Fixed projectile typo. Added callbacks for post_physics simulation

287 26/01/2014
Fixed packing of integers for off-by-one errors. 64 Bit integer handler was actually 32 bit

286 26/01/2014
Moved maximum id constants to replicables

285 21/01/2014
Converted to fixed timestep game loop Added tick property to WorldInfo

284 18/01/2014
Code cleanup Added Matrix support, fixed Quaternion support

283 06/01/2014
Added subclass_of caching (populated on change rather than each frame) Removed set and get simulated signals, now boolean argument. Supported changed roles over network to influence physics simulation type (requires explict set on server or possession/unpossession)

282 06/01/2014
Physics fixes Added projectile actor subclass

281 05/01/2014
Fixed a bug whereby RPC calls could not be overridden in base class. Fixed a bug from UI where the scores weren’t writing to the correct places. General clean up.

280 05/01/2014
Update WIKI information Renamed missing names

279 04/01/2014
Cleaned #PEP008 violations Cleaned signals.py invoke_targets method to become recursive, rather than wastefully creating a While loop. Added all method to network library module headers Moved all handlers into the native_handlers or serialiser modules in network. Renamed string and bytes handlers to include “handler”. Moved default value setting to AttributeContainer (from Attribute.register). Renamed ArgumentSerialiser to FlagSerialiser. Renamed NetmodeSelector to NetmodeSwitch. Removed bitarray support.

278 03/01/2014
Removed supply_data and has_data methods, instead using value placeholder “FindAttribute”. Removed lengthy network.attribute calls in bge_network.Replicables, instead imported namespace for now. Unified repr representation of objects. Renamed StaticValue to TypeFlag Cleaned server_validate method in PlayerController Added single attribute to denote max move id value (2**16) Fixed INT handler for non power of two integer bit lengths

277 03/01/2014
Fixed falloff for weapons Added staticmethod to Actors: from_object to return the actor reference. Fixed wrapping of classmethods and staticmethods

276 03/01/2014
Cleaned server logic. Removed manual timer code and implemented bge_network.Timer

275 03/01/2014
Added AttributeError for non existent InputManager attribute lookups. Removed Signals for network update. Added active property for the stream classes MicrophoneStream and SpeakerStream. Modified string representation of InstanceRegister, ReplicableRegister and Struct instances (the former now checks for registered state before bothering to print instance ID) Added immediate updates to matchmaker on connection and disconnection of clients, and deletion of server. Modified voice chat to occur on key press. Ensured voice chat clears queue when not streaming. Removed unnecessary super call in request_registration for conflicts in id resolution. Changed Notification to check message width in pixels rather than characters.

274 01/01/2014
Updated replication rules. Added additional docstrings

273 01/01/2014
Cleaned UI theme. Removed “True Lies.ttf” font. Included missing files from last commit

272 31/12/2013
Added new client UI features Added scrolling to notifications when overflow occurs

271 30/12/2013
Fixed netmode selector bug (reusing old classes) Cleaned up more code

270 29/12/2013
Code cleanup Added base class for netmode class selector

In this update, there have been a lot of functional changes. Collisions now occur between the player and physics actors. This makes rewinding and other physics code easier to work with. It also makes things more predictable by using a fixed timestep.

Some documentation is now available here http://pyauthserver.readthedocs.org/en/latest/.

i always wanted to make an online FPS game using BGE
will make one soon :wink:

Feb 27, 2014
Fixed replication bug (no limitation on attribute replication)
Cleaned netmode switch class
Added helper functions “annotate(name, value)(func)” and “get_annotation(func, name, default=None)” Cleaned GameLoop class - added profile context manager for restoring profiles

Feb 24, 2014
Support netmode-selected classes by default. (Now network.Connection, network.Channel and network.ConnectionInterface classes select by WorldInfo.netmode)
Forced NetmodeSwitch to include the TypeRegister metaclass by default
Cleaned NetmodeSwitch error messages.

Feb 22, 2014
Added fix to ensure replicables are not delayed in first replication
Fixed first person mouse look

Feb 20, 2014
Fixed parenting restoration in update_for Added arrow example blend 2x speed up for signal listener instantiation 1.5x speed up for Replicable instantiation

Feb 17, 2014
Fixed jitter buffer overflow error
Fixed voice chat problems
Added coloured particle trails
Added optional dict type and provide key arguments to FactoryDict

Feb 16, 2014
Fixed permission error forcing persistent references to unregistered replicables on the client
Created “particle” type - a non-replicable rigid body object.
dded demonstration health UI
Fixed replication error caused by priority system

Added support for single -element typed - sets and lists. Optional TypedSet and TypedList data types to protect against storing unserialisable values.
Added torn_off and replicate_temporarily attributes to replicables, the former enabling isolation of replication from new clients (to implement dead bodies for example) and the latter to initialise projectiles (which don’t need replicating thereafter).

  • Modified Signal update_graph to use while loops - cleaner than copying to memory recursively
  • Added support to deepcopy TypedIterable instances
  • Added ResourceActor to prevent specific actors from being deleted when their parent is.
  • Fixed unparenting from Actor parent’s.
  • Added support for custom navmesh rather than built-in (untested at this rev.)
  • Added GameInfo send_broadcast method (server only)
  • Removed unpack method in native_handler for Replicable (Deprecated)
  • Forced notifications to occur after all attribute replicationFixed serious error involving replication creation, updating and other.
  • Cleaned InstanceRegister metaclass and InstanceMixins mixin class (partially due to previous issue). IDs are still recycled but only once the ID range is exhausted, helping prevent torn_off and temporary replicables from causing ID conflicts. (Still needs to be implemented to protect against long-lasting torn_off and temporary replicables which may be deleted server side, releasing the ID).
  • Fixed bug where all booleans notify when individuals are changed.
  • Moved attribute notify callbacks to after updates.
  • Ensured that replication occurs at the same time replicables are created
  • General code cleanup

The latest example now implements a Capture the Flag mode. It’s basic - you can have two people owning the flag when they both collide with it, but the other player can be shot to take the flag. Parenting over the network isn’t yet implemented so the flag appears to float by the player on the other client’s screen.


how many dynamic scene elements do you think this could handle outside of people?

like dynamic scene elements that broadcast a property set that are it’s conditions?

I love battlefield 4’s destruction…

The short answer is that it depends. It will not be as capable as UDK or CryEngine for performance, but it will be great for multiplayer games.
You can dictate how performant it is by sending different amounts of data. You can have objects send their initial physics state and nothing after that, or you can send updates the entire time.

Here is the Flag used in my demo scene:


class CTFFlag(ResourceActor):
    owner_info_possessed = Attribute(type_of=Replicable,
                                     notify=True,
                                     complain=True)


    entity_name = "Flag"
    colours = {TeamRelation.friendly: [0, 255, 0, 1],
                TeamRelation.enemy: [255, 0, 0, 1]}


    def on_initialised(self):
        super().on_initialised()


        self.replicate_physics_to_owner = False


    def possessed_by(self, other):
        super().possessed_by(other)


        # Inform other players
        BroadcastMessage.invoke("{} has picked up flag"
                                .format(other.info.name))


    def unpossessed(self):
        # Inform other players
        BroadcastMessage.invoke("{} has dropped flag"
                                .format(self.owner.info.name))


        super().unpossessed()


    def on_notify(self, name):
        if name == "owner_info_possessed":
            player_controllers = WorldInfo.subclass_of(PlayerController)
            player_controller = next(player_controllers.__iter__())
            team_relation = player_controller.info.team.get_relationship_with(
                                         self.owner_info_possessed.team)


            self.colour = self.colours[team_relation]


        else:
            super().on_notify(name)


    def conditions(self, is_owner, is_complaint, is_initial):
        yield from super().conditions(is_owner, is_complaint, is_initial)


        if is_complaint:
            yield "owner_info_possessed"

It has a number of important parts. There is the possessed_by and unpossed callbacks. These are run when the player who captures the flag disconnects or dies, or picks the flag up. The flag is dropped / parented to the player.

When it is parented on the server, the server sets the “owner_info_possessed” network Attribute to refer to the information about the player who just took the flag. In on_notify, when that value is received by the client it finds the local player for that client (there is only one per client game) and it works out the relationship between itself and the player who took the flag. If it’s a friendly team, the colour is green, otherwise it is set to red.

In the conditions function, we yield the names of attributes that we want the server to send to the client (If they’ve been changed). We check if the value has actually been changed before we yield it (in this example). This is done by the network code anyway, but that code will run a check on it each time if we yield it all the time, so if we run the check in the conditions function we save processing time (as that value is a simple boolean).

There still seems to be a dangerous bug lurking. I’m looking for it.

This bug has now been fixed. It was an off by one error, causes by a persistent flag which stated a variable was set to none in the data. When a variable is set to none we don’t read it’s value, hence we were not reading a byte (in this case) from the buffer when we should have been.

As well as these bug fixes, the system is much more robust and feature complete.

Sent from my Nexus 5 using Tapatalk

Latest changes:

  • Updated Docstrings.

  • Fixed serious bug with RPC inheritance : New behaviour has been tested -
    [LIST=1]

  • MarkedAttributes will correctly function across inheritance trees.

  • Accessing an RPC function which hasn’t been registered (such as the super() of a child function) will return the original function.

  • Modifying MarkedAttribute data no longer involves modifying the function signature.

  • Cleaned some code in the rpc module.

  • Modified String and Bytes handlers so that we can perform inheritance.

  • Added reset() method to RenewableGenerator.

  • Renamed RPCInterfaceFactory.function to RPCInterfaceFactory.original_function
    [/LIST]

Latest changes:

  • Further Code cleanup
  • Refactored InputManager (now demands status_lookup argument). Defines new class
  • BGEInputStatusLookup.
  • Changing camera mode immediately moves rotation and location of camera
  • Fixed WorldInfo replication issue (always_relevant was cleared in inherited initialiser).
  • Delegated channel specific attributes to Channel (slower than calculation in the
  • Connection, but an additional speed boost mitigates this compared to previous performance; using time.monotonic() in often-iterated code)
  • Modified camera bug fix code to use context manager
  • Renamed InputManager TypeFlag argument input_fields to fields.
  • Renamed bits to max_bits for Int TypeFlag argument.
  • Modified system to use Struct for Moves - saves maintaining two APIs, and reduces hacky code in input conversion. This requires PlayerController subclasses to define movement_struct in the class definition (from PlayerController.create_movement_struct(*fields)). This does seem to be slower server-side, not sure quite why yet, need to profile code.
  • Added MarkAttribute lookup for TypeFlag type argument as well as TypeFlag data values.
  • Added error when looking for the handler of a NoneType (object without mro)
  • Expanded UnitTests to check bitfields and all int packers. (More to come)