Saturday, March 14, 2009

C++ pointer-to-member-function events

I made an Event class that can be hooked with a member function from any class. I ended up using a combination of virtual functions, templates, and nested classes, and I was just pleased enough with the result to blag about it for a while.

Static example

class Foo {
private:
  Event _wasFrobbed;
  
public:
  Foo() { }
  Event::PublicEvent& wasFrobbed() { return _wasFrobbed.pub(); }
  void Frob() { _wasFrobbed.Fire(); }
};

void CharlesWasFrobbed() {
  cout << "Charles was frobbed\n";
}

void Test1() {
  Foo charles;
  
  //Hooking his event
  charles.wasFrobbed().Hook(&CharlesWasFrobbed);
  
  //Frobbing him
  charles.Frob();
  
  //Unhooking his event
  charles.wasFrobbed().Unhook(&CharlesWasFrobbed);
}

That's easy enough to use, but not so impressive on its own. The fun part is that it works with pointers to member functions as well!

Pointer-to-member-function example

class Bar {
private:
  Foo* _pfoo;
  
public:
  void NoticeFrob() {
    cout << "a Bar was witness to a frobbing\n";
  }
  
  Bar(Foo* pfoo) : _pfoo(pfoo) {
    //Hook the target Foo's event
    _pfoo->wasFrobbed().Hook(this, &Bar::NoticeFrob);
  }
  
  ~Bar() {
    //Unhook the target Foo's event
    _pfoo->wasFrobbed().Unhook(this, &Bar::NoticeFrob);
  }
};

void Test2() {
  Foo winston;
  Bar nigel(&winston);
  
  //Frobbing Winston ... Nigel will notice
  winston.Frob();
}

Next step: add a template parameter to the Event class itself so the event can fire with an argument.

Sunday, February 1, 2009

Networking model

I've been reading up on how Valve's Source engine handles network play, and I've come up with an outline for how I want this to work in Project Mod.

The key feature is a deliberate addition of input latency, sacrificing some snappiness for a smoother appearance on the client. Clients sample the local player's controls and send them to the server at the world's step rate (200 Hz at the moment, one step per 5 ms), but tagged for a future step. Source defaults to 100 ms. The server operates in the future, collecting controls packets, simulating the world, and sending periodic bunched updates to all clients.

When network latency is less than (input latency - update interval) and no packets are dropped, every client will already have the current world state stored in a buffer when it's time to refresh the screen. This means no prediction, no extrapolation, and zero jittering for ping up to 80 ms, at the cost of a consistent input latency of 100 ms. This delay could of course be adjusted for connections with higher or lower average latency, or even disabled for a local single player game.

I'll post more as I attempt to implement it.

Saturday, January 17, 2009

Saturday, January 10, 2009

Network testing

I got my UDP networking code functional on a "brute force and who cares if we clog the intertubes" level. It sends 200 full game state updates per second, and the clients spit out controls packets (containing, of all bandwidth wasting things, ASCII strings) as quickly as they can generate them. So I went to #wiidev and got a helper on board to fire up the Wii client from an external network:


Each of those crates is under the control of one client. One is a Linux client running on the same machine as the game server. One is a Wii client on the same LAN as the game server, and one is a Wii client about 90 ms away over the internet. The screenshot was captured by the Linux client.

Friday, January 9, 2009

Filesystem layer and menus

I implemented most of a filesystem layer. It gives me uniform access to a media tree on Linux/Windows local filesystem, Wii front SD card slot, Wii USB flash drive, Wii/GameCube memory card files, and the media server I'll keep running. To configure this thing I wrote a simple menu module that pops up on startup to let you pick an FS module and, on some modules, browse for a root directory.



I'm thinking a list of default locations (~/.mod2, sd:/apps/mod2, %whatever-is-the-shortcut-for-application-data%\mvanbem\mod2, etc.) combined with a simple .txt config file would make this screen usually skippable.

I'm hard at work getting some netcode and a useful Player object going. At the moment I can connect to the game server and show unpredicted snapshots on the client's screen. No interactivity yet.

Monday, January 5, 2009

YouTube poop: The Birth of Weegee

Yesterday I was inspired with a vision, so I fired up my XP VM and spent all day fighting with Windows Movie Maker. I also used a healthy dose of the GIMP, but that went much more smoothly. This is what happened:



Here's its page on YouTube. Rate me!

Thursday, January 1, 2009

More configure tricks

I previously set up all of the libs needed to compile Project Mod on my Windows XP VirtualBox instance, but it takes forever to compile, so I'm trying once again to set up cross compiling from Linux. It turns out that lots of libs will compile just fine if you pass in a few flags when you configure them:

./configure --host=i586-mingw32msvc --prefix=/usr/i586-mingw32msvc

Of course, more will go wrong, so you have to be willing to dive into the configure script and search for override flags. Some are easier, like how SDL_net requires --disable-sdltest. Without that flag the configure script will try to run a test program, which will fail since it's not producing Linux executables. Others are a bit trickier:

ac_cv_lib_png_png_create_read_struct=yes ./configure --host=i586-mingw32msvc --prefix=/usr/i586-mingw32msvc --disable-sdltest

SDL_image needs libpng for PNG image support, but it compiles a test program to check if libpng works. I know that libpng is fine (I just compiled it), so I made it skip the check by defining_that_long_environment_variable. This command makes the configure script spit out:

checking png.h usability... yes
checking png.h presence... yes
checking for png.h... yes
checking for png_create_read_struct in -lpng... (cached) yes

Without the environment variable that last line fails and PNG support is left out.