New Era of Gaming: FujiNet Game Server

Recently a flurry of activity on the gaming front has resulted in development, deployment, and usage of the FujiNet Game Server system and a working client for Atari 8-bits. More client platforms are planned soon. The first working Game Server is an implementation of Five Card Stud. More games are also planned now that there is one amazing example.

Screen Shot 2023 06 19 at 10 27 15 PM

Updates

Updated: July 3rd to point to the new official lobby.xex host on fujinet.online TNFS. The lobby game server itself is now also hosted on fujinet.online systems.

To get to the point- you can play right now- with an Atari 8-bit and your FujiNet- with other human players and a coven of bots if no humans are available.

We are informally coordinating communal game plays in our Discord, in the #developing-game-servers room. I try to play a bit every night around 10pm EDT.

How To Play Right Now

To play simply boot up CONFIG add a HOST slot (if you don’t already have it): fujinet.online
Navigate to the fujinet.online server and inside the ATARI folder is the lobby XEX- you should mount the _lobby.xex into the Drive 1 slot of your FujiNet.

Screen Shot 2023 06 21 at 2 36 31 PM
Atari CONFIG

Now boot, continuing to hold OPTION to disable BASIC if your Atari has BASIC built-in.

You will be presented with the Lobby – this displays the attached Game Servers (more below). Select a room – the Lobby shows how many open human slots are available in each room. Some room have bots in them so you can always play a game no matter if human players are present or not.

Once you select a Room and then hold OPTION key the LOBBY will chain-load the 5cardstud.xex for you automatically.

Your Atari will boot up, connect to that server (each room is its own server instance) and be placed at the table. Now regular 5 card stud play continues. Hit ESC any time to get to the in-game menu where you can leave the table or quit the game. If you leave the game you will have a chance to now join another room without having to load up LOBBY.XEX.

Screen Shot 2023 06 20 at 8 41 06 PM
5cardstud connect screen

Screen Shot 2023 06 20 at 8 44 06 PM
ESC Menu Screen to leave

Screen Shot 2023 06 20 at 9 06 05 PM
Table Join Screen to switch tables

Screen Shot 2023 06 20 at 9 16 32 PM
Typical hand of poker with six bots and two humans (Thom and myself)

Interested? Hop on Discord and find out when someone is available to play.

What is Going on Here?

There are a few elements to the FujiNet Game System (FGS) – the Lobby, a Lobby Client, Game Servers and Game Clients.

The Idea

Three years ago Thomas CherryHomes had an idea about a centralized Game Server that all platforms could connect to using FujiNet over the Internet. It would have various Game available to connect to and play with human opponents. His skeleton code lives over at the server GitHub repo. Eventually enough people had FujiNets that some of them wanted to play together over the network and they started to collaborate on the Discord.

The Lobby Server

rogersm has lead the Lobby effort. He picked up on the idea that Thom CherryHomes had 3 years ago for a system of game servers and clients apps across the FujiNet platform ecosystem. Roger went off and implemented the Lobby Server portion of the FGS which is the critical hub and central location for discovery of any running Game Servers.

It’s written in Go and is located on the FujiNet GitHub in the servers repo. It defines and implements the LOBBY Spec for Game Servers to interact and register themselves with the lobby. The lobby spec is also used by the Lobby Clients to allow local discovery and then connecting to running servers to play.

Lobby Clients

The lobby clients are platform specific apps (currently just for Atari 8bit) that when launched connect to the Lobby and facilitate game server selection. The Lobby client is designed to handle future game servers without needing updates.

Lobby client app for Atari
The Lobby Client for Atari 8-bit

Eric Carr has written an Atari 8-bit Lobby Client and its source is available on the GitHub fujinet-apps repo. The XEX is available on ec.tnfs.io to load and run right now.

Eric implemented user persistence using the FujiNet AppKey so that you will always be you after registering your name to the Lobby the first time you start. You need a SD card inserted into your FujiNet for the AppKey functionality. Eric wrote the lobby client in fastbasic.

fastbasic is the amazing modern BASIC project created by Daniel Serpell (dsmc) in 2017. It’s bytecode based compiler and can be cross-compiled from a modern PC. More info on fastbasic is available on the Atariwiki and Daniel’s GitHub repo.

Game Servers

Eric Carr continued his programming efforts and created the first Game Server- an implementation of 5 card stud poker. It’s up in the FujiNet servers repo in GitHub. It’s also written in Go. It implements itself as a number of server instances each with it’s own mix of bots or no bots at all. Eric’s game server is very complex and handles not only bots, the 5 card stud rules for the game experience but also entering and leaving tables, time-outs for players not responding and allowing errant FujiNet clients to reconnect — most times seamlessly — to existing in-play hands. It’s a remarkable server with tons of features. And apparently Eric was only getting started…

Game Clients

The client application loads on your 8-bit platform of choice (at this point in time it’s just Atari 8-bit) and using FujiNet connects to a Game Server. You would have already had to have loaded the a Lobby client to pick a game server. Eric Carr has created a fully featured 5 card stud client for Atari 8bit computers again using fastbasic. And it’s simply amazing.

Eric’s code is up on GitHub in the fujinet-apps repo. This is an amazing example of a full-featured fastbasic app that uses custom fonts, DLI and has very detailed fit and finish. The response of the interface is snappy and the flow and game play, while dictated for the most part by the rules of 5 card stud, is just easy and nice to play on the Atari.

Screen Shot 2023 06 20 at 9 09 38 PM
Waiting for a Bot to decide, but I’ve won this round with my pair

The clients are for the most part dumb, all gameplay logic happens on the server and the app is in charge of interpreting the status send in JSON from the server though the FujiNet’s JSON parser and updating the clients screen, handing input (which is just cursor keys and ENTER, ESC) and polling the server for updates. It works amazing well as various wifi issues can cause momentary drops but the client can reconnect and resync up very nicely to an in-progress game.

Below is some captured output from the FujiNet device as it polls and receives a round of data from the Game server.

What is Next?

This first implementation of the FGS is a whole other level of tight, collaborative, and fun interaction that enables these older 8-bit platforms to participate in modern, networked, cross-client, gaming scenarios. There are plans for a C64, Vic20 and eventual Apple2, and ADAM clients for this initial game as soon as possible. If you are ready to help and want to see this on a platform then consider joining the Discord and helping us code them up!

Screen Shot 2023 06 21 at 3 16 48 PM

Sample FujiNet Output

Here is output of the FujiNet showing a round of data from the Game Server to the Client. This is an end-of-hand message as you can see that I won the hand (“lastResult”:” ANDYEMU won by default).

21:04:47.078710 > ACK!
21:04:47.078719 > mgHttpClient::GET
21:04:47.078723 > 00143441 _perform
21:04:47.266835 > mgHttpClient: Host name resolved
21:04:47.287141 > mgHttpClient: Connected
21:04:47.333274 > mgHttpClient: Data written
21:04:47.397333 > mgHttpClient: HTTP response
21:04:47.397354 >   Status: 200
21:04:47.397358 >   Received: 882
21:04:47.397361 >   Body: 361 bytes
21:04:47.397368 >     buffer malloc(361)
21:04:47.397386 > mgHttpClient: Data received
21:04:47.397390 > mgHttpClient: Connection closed
21:04:47.397550 > 00143580 _perform status = 200, length = 361, chunked = 0
21:04:47.397559 > NetworkProtocolFS::read_file(361)
21:04:47.397563 > NetworkProtocolHTTP::read_file_handle(0x12b904f90,361)
21:04:47.397567 > NetworkProtocolHTTP::read_file_handle_data()
21:04:47.397572 > NetworkProtocol::read(361)
21:04:47.397577 > S: {"lastResult":" ANDYEMU won by default","round":1,"pot":3,"activePlayer":2,"moveTime":0,"viewing":0,"validMoves":null,"players":[{"name":" ANDYEMU","status":1,"bet":5,"move":"BET","purse":326,"hand":"8H2C"},{"name":"Clyd BOT","status":1,"bet":5,"move":"CALL","purse":313,"hand":"??5S"},{"name":"Kirk BOT","status":1,"bet":0,"move":"","purse":60,"hand":"??2D"}]}
21:04:47.397622 > Parsed JSON: {
	"lastResult":	" ANDYEMU won by default",
	"round":	1,
	"pot":	3,
	"activePlayer":	2,
	"moveTime":	0,
	"viewing":	0,
	"validMoves":	null,
	"players":	[{
			"name":	" ANDYEMU",
			"status":	1,
			"bet":	5,
			"move":	"BET",
			"purse":	326,
			"hand":	"8H2C"
		}, {
			"name":	"Clyd BOT",
			"status":	1,
			"bet":	5,
			"move":	"CALL",
			"purse":	313,
			"hand":	"??5S"
		}, {
			"name":	"Kirk BOT",
			"status":	1,
			"bet":	0,
			"move":	"",
			"purse":	60,
			"hand":	"??2D"
		}]
}
21:04:47.398008 > COMPLETE!
21:04:47.398020 > SIO CMD processed in 322 ms
21:04:47.398025 > -+
21:04:47.427923 > 
21:04:47.429228 > CF: 78 51 0c 00 d5
21:04:47.429240 > sioNetwork::sio_process 0x51 'Q': 0x0c, 0x00
21:04:47.429245 > inq_dstats = 128
21:04:47.429249 > ACK+!
21:04:47.429253 > <-SIO read 256 bytes
21:04:47.564837 > ACK!
21:04:47.564863 > S: [cJSON_IsString]  ANDYEMU won by default
21:04:47.564876 > S: [cJSON_IsNumber] 1.000000
21:04:47.564886 > S: [cJSON_IsNumber] 3.000000
21:04:47.564892 > S: [cJSON_IsNumber] 2.000000
21:04:47.564896 > S: [cJSON_IsNumber] 0.000000
21:04:47.564902 > S: [cJSON_IsNumber] 0.000000
21:04:47.564907 > S: [cJSON_IsNull]
21:04:47.564913 > S: [cJSON_IsString]  ANDYEMU
21:04:47.564917 > S: [cJSON_IsNumber] 1.000000
21:04:47.564922 > S: [cJSON_IsNumber] 5.000000
21:04:47.564926 > S: [cJSON_IsString] BET
21:04:47.564931 > S: [cJSON_IsNumber] 326.000000
21:04:47.564936 > S: [cJSON_IsString] 8H2C
21:04:47.564941 > S: [cJSON_IsString] Clyd BOT
21:04:47.564946 > S: [cJSON_IsNumber] 1.000000
21:04:47.564950 > S: [cJSON_IsNumber] 5.000000
21:04:47.564955 > S: [cJSON_IsString] CALL
21:04:47.564959 > S: [cJSON_IsNumber] 313.000000
21:04:47.564963 > S: [cJSON_IsString] ??5S
21:04:47.564968 > S: [cJSON_IsString] Kirk BOT
21:04:47.564973 > S: [cJSON_IsNumber] 1.000000
21:04:47.564977 > S: [cJSON_IsNumber] 0.000000
21:04:47.564982 > S: [cJSON_IsString] 
21:04:47.564986 > S: [cJSON_IsNumber] 60.000000
21:04:47.564991 > S: [cJSON_IsString] ??2D
21:04:47.564998 > S: [cJSON_IsString]  ANDYEMU won by default
21:04:47.565004 > S: [cJSON_IsNumber] 1.000000
21:04:47.565008 > S: [cJSON_IsNumber] 3.000000
21:04:47.565012 > S: [cJSON_IsNumber] 2.000000
21:04:47.565017 > S: [cJSON_IsNumber] 0.000000
21:04:47.565021 > S: [cJSON_IsNumber] 0.000000
21:04:47.565025 > S: [cJSON_IsNull]
21:04:47.565029 > S: [cJSON_IsString]  ANDYEMU
21:04:47.565033 > S: [cJSON_IsNumber] 1.000000
21:04:47.565038 > S: [cJSON_IsNumber] 5.000000
21:04:47.565042 > S: [cJSON_IsString] BET
21:04:47.565046 > S: [cJSON_IsNumber] 326.000000
21:04:47.565051 > S: [cJSON_IsString] 8H2C
21:04:47.565055 > S: [cJSON_IsString] Clyd BOT
21:04:47.565059 > S: [cJSON_IsNumber] 1.000000
21:04:47.565064 > S: [cJSON_IsNumber] 5.000000
21:04:47.565068 > S: [cJSON_IsString] CALL
21:04:47.565072 > S: [cJSON_IsNumber] 313.000000
21:04:47.565076 > S: [cJSON_IsString] ??5S
21:04:47.565081 > S: [cJSON_IsString] Kirk BOT
21:04:47.565085 > S: [cJSON_IsNumber] 1.000000
21:04:47.565090 > S: [cJSON_IsNumber] 0.000000
21:04:47.565094 > S: [cJSON_IsString] 
21:04:47.565098 > S: [cJSON_IsNumber] 60.000000
21:04:47.565102 > S: [cJSON_IsString] ??2D
21:04:47.565108 > S: [cJSON_IsString]  ANDYEMU won by default
21:04:47.565113 > S: [cJSON_IsNumber] 1.000000
21:04:47.565118 > S: [cJSON_IsNumber] 3.000000
21:04:47.565122 > S: [cJSON_IsNumber] 2.000000
21:04:47.565127 > S: [cJSON_IsNumber] 0.000000
21:04:47.565131 > S: [cJSON_IsNumber] 0.000000
21:04:47.565135 > S: [cJSON_IsNull]
21:04:47.565139 > S: [cJSON_IsString]  ANDYEMU
21:04:47.565143 > S: [cJSON_IsNumber] 1.000000
21:04:47.565148 > S: [cJSON_IsNumber] 5.000000
21:04:47.565152 > S: [cJSON_IsString] BET
21:04:47.565157 > S: [cJSON_IsNumber] 326.000000
21:04:47.565161 > S: [cJSON_IsString] 8H2C
21:04:47.565166 > S: [cJSON_IsString] Clyd BOT
21:04:47.565170 > S: [cJSON_IsNumber] 1.000000
21:04:47.565175 > S: [cJSON_IsNumber] 5.000000
21:04:47.565179 > S: [cJSON_IsString] CALL
21:04:47.565184 > S: [cJSON_IsNumber] 313.000000
21:04:47.565188 > S: [cJSON_IsString] ??5S
21:04:47.565193 > S: [cJSON_IsString] Kirk BOT
21:04:47.565197 > S: [cJSON_IsNumber] 1.000000
21:04:47.565202 > S: [cJSON_IsNumber] 0.000000
21:04:47.565206 > S: [cJSON_IsString] 
21:04:47.565210 > S: [cJSON_IsNumber] 60.000000
21:04:47.565214 > S: [cJSON_IsString] ??2D
21:04:47.565221 > Query set to 
21:04:47.565553 > COMPLETE!
21:04:47.565562 > SIO CMD processed in 138 ms
21:04:47.565568 > -
21:04:47.578607 > 
21:04:47.579977 > CF: 78 53 0c 00 d7
21:04:47.579990 > sioNetwork::sio_process 0x53 'S': 0x0c, 0x00
21:04:47.580327 > ACK!
21:04:47.580334 > sioNetwork::sio_status_channel(1)
21:04:47.580338 > sio_status_channel() - BW: 280 C: 1 E: 1
21:04:47.580342 > ->SIO write 4 bytes
21:04:47.580675 > COMPLETE!
21:04:47.580693 > SIO CMD processed in 2 ms
21:04:47.585758 > +
21:04:47.594415 > 
21:04:47.595686 > CF: 78 52 18 01 e3
21:04:47.595698 > sioNetwork::sio_process 0x52 'R': 0x18, 0x01
21:04:47.595703 > sioNetwork::sio_read( 280 bytes)
21:04:47.596036 > ACK!
21:04:47.596044 > ->SIO write 280 bytes
21:04:47.596376 > COMPLETE!
21:04:47.596397 > SIO CMD processed in 2 ms
21:04:47.605262 > -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

2 Comments

  • Norman says:

    The tnfs site address has changed from ec.tnfs.io to fujinet.online and the executible is now _lobby.exe in the ATARI folder.

  • Luis Rodrigo Carrasco Lagos says:

    Hello, I would like to ask if I could implement this on my TNFS server. Stay tuned.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.