Code Monkey home page Code Monkey logo

cerveau's Introduction

Cerveau

The Node.js implementation of a multi-game server intended for games with AIs within the Cadre framework using TypeScript.

{Cadre}

All inspiration taken from Missouri S&T's SIG-GAME framework, and most of the terminology is assuming some familiarity with it as this is a spiritual successor to it.

Features

  • Multi-Game Multi-Framework
    • One server instance can host multiple games at the same time, and the games can be completely different games.
    • e.g. One server could host Chess, Checkers, Go, etc all at the same time and clients can choose which games to play.
  • A simple Node.js app: Simple to write, fast to run, and very easy to develop in.
  • Easy generation of new games using the Creer codegen.
    • Most of the game code is easy to replace using generatedObjName.js files to ease Creer codegen re-runs.
  • Games can be turn based or real time, with turn based code abstracted into an inheritable class.
    • More game prototypes can be added easily as new classes for re-use
  • Non-restrictive game structures.
    • Games state can be structured in any way.
    • Cycles can be created and synched easily between the server and its clients.
  • All logic is server side (here), with states updating for clients after anything changes.
    • Optional support for client side logic (though this is generally frowned upon).
  • Web interface to check on gamelogs and server status.
  • Leverages TypeScript for type safety and eases developer on-boarding.
  • Latest ES features by keeping up to date with Node.js releases.
  • Truly multi-threaded. As opposed to using Node's internal thread pool, each game session is spun off to a separate process to run until the game is complete.
  • Networking via TCP
    • Communication via json strings with support for cycles within game references.
    • Only deltas in states are send over the network.
  • Automatic gamelog generation as a json structure of deltas
  • Extra fields present for Arena and Data Mining purposes
  • Optional authentication with separate web server for "official" game matches.

Developing

Developing is super easy, especially if you just want to code games.

We recommend using Visual Studio Code as your IDE with the ESLint plugin. It comes with everything you need to build, run, and debug the game server.

After that you don't need much knowledge of JS/TS. Visual Studio Code, and the linter (Eslint) will yell at you, and suggest code, to help guide you. Most game logic can be written by students new to programming, as 99% of the heavy lifting is handled by the core of this project.

Otherwise this is a Node.js + TypeScript project. If you want to use your own custom environment you should understand how to build and run TypeScript projects.

Requirements

The node version defined in .nvmrc is the latest supported version. It may run on older/newer versions, but we do not support them officially.

How to Run

npm install
npm run build
npm start

That's it, your server is running! Now start up two Cadre clients such as the Python and the JavaScript clients and connect them to the new server. Or visit the server in your web-browser to view gamelogs and the status of the game server.

By default games can be played on port 3000, and the website starts on the game port + 80, so 3080 by default. Websocket clients on port 3088.

Faster development

By default, using npm run live has a long startup time as each file has to be transpiled by TypeScript before being ran. If you are actively developing a game your best workflow will be to open two terminals (split view in VSC):

First Terminal = Watcher

This will watch for code changes, and re-compile them to JS when a file changes.

npm run build:watch

Second Terminal = Run Transpiled Code

npm start

With these two terminals up are you good to go! Because by default games are multi-threaded, if you make a change to a file, the next time you play that game the new code will run. You don't have to tear down the npm run js process!

How to add games

Use Creer to generate some base code given the basic game structure.

Then fill in the functions for the functions to you make in your game objects. Everything else should be handled by the base classes in both this server and the clients.

Implementing game logic

Once you've generated a game, and generated its files via Creer, and bunch of files will be generated that need you to add logic to.

So for example, if you have a Unit class with a move(tile) function, you'd go to src/games/game-name/unit.ts.

Then you'd look for two functions: invalidateMove() and move().

In the invalidate function, examine each argument and the game state to try to find a reason why the passed in arguments are invalid. If you can find a reason (such as tile === undefined), return a string explaining to the coder why we couldn't run their function.

Then fill out the actual game logic in the move() function. That only gets called if the invalidate function doesn't return an error string. So you can safely assume if it is invoked, everything is ok.

Other than that, the game.ts, game-manager.ts, and game-settings.ts files need to be filled out as well.

game.ts should initialize the game, which probably means map generation and randomization.

game-manager.ts "glues" everything together. It manages the game, and everything in it. So that is where you should code in-between turn logic, creating new things, checking for game over, etc.

game-settings.ts is optional, however it exposes variables coders can tweak to customize they game they are playing. This is useful for them to debug, and for you to work faster as you can send constants to the game server to balance games faster (like a unit damage setting).

Debugging

We've included a debugging profile for Visual Studio Code that requires no additional configuration. Just open this project in VSC, and hit "Start Debugging" (F5 by default).

VSC will hook into this using the v8 debugger, and you can place breakpoints anywhere to see what is happening. A bunch of settings are enabled/disabled to make debugging easier, like not timing out clients so you can poke around in paused code for long periods of time without killing clients.

cerveau's People

Contributors

ampx2c avatar arnpxk avatar bryce-foster avatar cjhbh3 avatar dylhamp avatar efmcmahon avatar epicpants avatar hannahstroble avatar jacktilded avatar jacobfischer avatar jawbone999 avatar jeffreystrahm avatar jzaustin295 avatar leoflaker avatar mastercash avatar matthewqualls avatar mbzzd avatar mikeskull avatar petersonkat avatar ragnyll avatar reginaldjefferywatson avatar russleyshaw avatar shimmeringhydra avatar shwam avatar tarnasa avatar tehpers avatar thomas-mckanna avatar tpfvy7 avatar user404d avatar week3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cerveau's Issues

Error when open http://localhost:3080/

Hi!
First of all, It is really awesome framework! Thanks so much for bring it to us!
I am trying to setup game server. When i start it, it runs fine
Lobby @ 13799 Found game 'Checkers'.
Lobby @ 13799 --- Webserver running on localhost:3080 ---
Lobby @ 13799 --- Lobby listening on localhost:3000 ---
But when open web server, we have bellow error, please help me, i just on the way to learn node js, thank so much!

TypeError: Cannot use 'in' operator to search for 'data' in null
at Object.compile (/Users/taka/github/Cerveau/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:467:19)
at HandlebarsEnvironment.hb.compile (/Users/taka/github/Cerveau/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars.js:38:41)
at ExpressHandlebars._compileTemplate (/Users/taka/github/Cerveau/node_modules/express-handlebars/lib/express-handlebars.js:239:28)
at ExpressHandlebars. (/Users/taka/github/Cerveau/node_modules/express-handlebars/lib/express-handlebars.js:124:25)
TypeError: Cannot use 'in' operator to search for 'data' in null
at Object.compile (/Users/taka/github/Cerveau/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:467:19)
at HandlebarsEnvironment.hb.compile (/Users/taka/github/Cerveau/node_modules/express-handlebars/node_modules/handlebars/dist/cjs/handlebars.js:38:41)
at ExpressHandlebars._compileTemplate (/Users/taka/github/Cerveau/node_modules/express-handlebars/lib/express-handlebars.js:239:28)
at ExpressHandlebars. (/Users/taka/github/Cerveau/node_modules/express-handlebars/lib/express-handlebars.js:124:25)

Connecting to game lobby (port 3000 by default) with web browser crashes server

Here is the error it produces (personal information expunged)

...\Cadre-MegaMinerAI-Dev\Cerveau\gameplay\lobby.js:288
        client.gameSession.clients.removeElement(client);
                          ^

TypeError: Cannot read property 'clients' of undefined
    at Object.Class.clientDisconnected (...\Cadre-MegaMinerAI-Dev\Cerveau\gameplay\lobby.js:288:27)
    at Socket.socketListenerOnClose (...\Cadre-MegaMinerAI-Dev\Cerveau\gameplay\client.js:60:25)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at TCP._onclose (net.js:469:12)

Chess: Actual threefold rule triggered as 50-move rule

The function used to handle 50-move rule using internal check for actual threefold rule which forces both the simplified and non simplified threefold rule to be followed else trigger a draw for full threefold rule disguised as a 50-move draw.

Disconnect cheesing clients

If a client does something cheesy, like send a string that is a reserved delta merge keyword; disconnect them. It could possibly lead to erroneous gamelogs and the only real reason those strings would be sent is to screw with the game server.

Gamelogs don't generate on Mac OS X

Apparently on Mac OS X gamelog files are not generated. Logs are using the --log arg, but even though no errors claim to happen no files are dumped in output/gamelogs according to @takaaptech in siggame/Joueur.lua#2. Basically run two lua clients against each other playing checkers. We expect a gamelog to be generated. Instead none get output.

As I don't have access to this OS, could someone with an Apple machine check this out? takaaptech theorized that maybe the game session thread process was not properly sending data back to the main thread.

I'm more than willing to sit down with any devs to sort this out.

Default visualizer-url

Currently the arg parm visualizer-url is an option.

However we can expect the visualizer will exist at vis.megaminerai.com now.

So have that be the default value, and allow them to override it should they want to.

Upgrade to Node.js v6

It is untested beyond Node.js 4 LTS, 6 is supposed to be the next LTS, so we should start migrating. Plus there's all those cool ES6 features!

Add Gamelog deletion endpoint

Currently you can GET gamelogs via an HTTP GET Request to /gamelog/:id, and get some Json. cool, makes sense.

You should be able to delete those gamelogs too. This is useful for the arena to clean up the gamelogs directory after it ships them off to the magical cloud land.

So, if a HTTP DELETE Request is made at /gamelog/:id, delete it! Respond with 200 OK if, well, ok.

Does Carveau support for Human vs Human

Hi,
So far, i can setup two AI in a game. And i wonder if Carveau could support Human and Human or Human and AI in game? If yes, how can we implement that? Thank so much!

auto updater prints fatal error outside of git repo

  1. Run Cerveau outside of a git repo
  2. You'll get the following printed to the screen:
fatal: Not a git repository (or any of the parent directories): .git

The next line will be correct:

[18:37:41.856]  Lobby @ 12164  Updater Warning: Error trying to use Git, is it installed on your system?

Not a huge issue, but should be investigated what is printing that, and remove/fix it.

Spectate Games in Progress

Currently, games must be connected to to spectate before the game starts. Now, we should allow spectators to join in progress games.

Remove --host

Currently the host name needs to be passed via --host

However that's silly. Connecting clients know the hostname, they had to use it to connect.

So instead send some string replace like %%HOSTNAME%% and enhance all clients to replace that with their hostname. With that done this feature can be removed, and it simplies sending gamelog urls at the end of games.

Graceful Shutdown

When Cerveau gets a SIGINT (Ctrl + C) it should gracefully shut down. That means it should:

  1. Disconnect any Lobbied clients with a fatal event, and a message explaining the server is shutting down.
  2. Wait for all game sessions to exit, and log them as normal
  3. If any more connections come in while waiting to shut down give them the same fatal event as in 1, then disconnect them.

Once all game sessions have exited gracefully Cerveau can shut down too, and no one is forced out of a game.

If another SIGINT is received during this shutdown it should just force shutdown the whole thing.

Note: Node.js exposes an easy hook for the SIGINT event: https://nodejs.org/api/readline.html#readline_event_sigint

Add unit tests

Much of the base framework has matured to the point it should be unit tested.

Here's a vague list of things that would be possible to test:

  • Networking I/O
  • RESTful API
  • Delta parsing/creation
  • Matured games like Chess

I think it would be unwise to unit test in development games, as they are evolving so fast we don't necessarily know what to test. However, a known game like Chess could be tested by sending pre-determined moves and ensuring that the game state changes in expected ways.

Unit testing the game server would be tricky as it's only half of the problem. There's also a client you have to connect to do much of anything, and if that is broke the unit tests could break, claiming the game server is wrong.

So, someone should investigate how easy testing is to add, and if it even works reliably.

Server crashes upon recieving an invalid string

For example, here is the error generated when sending the string "hi\x04" to the server:

undefined:1
hi
^

SyntaxError: Unexpected token h
    at Object.parse (native)
    at Object.Class._onSocketData (Cerveau\gameplay\tcpClient.js:30:51)
    at Socket.onSocketData (Cerveau\gameplay\client.js:34:18)
    at emitOne (events.js:77:13)
    at Socket.emit (events.js:169:7)
    at readableAddChunk (_stream_readable.js:146:16)
    at Socket.Readable.push (_stream_readable.js:110:10)
    at TCP.onread (net.js:523:20)

Gamelog Pass-Through

Currently the gamelog api is as follows:

host/gamelog/gameName/gameSession/[epoch]

While, that's cool, because theoretically someone could poke through gamelogs via session, that's not realistic. All the other siggame apps using this endpoint will have the identifier they need to grab a gamelog.

So instead move the gamelog api to:

host/gamelog/filename

where filename is the name of the file (without the .json.gz) in the output/gamelogs directory. Hell we might be able to just expose that folder and set the HTTP Response header's Content-Encoding: gzip so we just dump those bytes on them. That would be ideal.

Basically the current way is over-complicated for no reason.

Delta Mergeable Dictionaries do not work

Basically, any dictionary/map made in a game template will not work. The only exception is the gameObjects dict as it is special, hence why it's not been seen as a bug yet.

They work server side, but deltas are not registered, and thus not sent to clients, so state gets out of sync.

Error in url from Cerveau web interface for game logs

The view and download buttons for games that have been played do not work as expected.
The download has HOSTNAME instead of the actual hostname.
The view has :3080 written twice in the url sent to the visualizer for the log location.

Game Object Factory does not register array contents from constructor args

Let's say we have the following game object:

Thing:
  attributes:
    numbers: const int[]

then in the game code we create a Thing via a GameManager:

manager.create.thing({
  numbers: [1, 3, 3, 7],
});

We'd expect a new Thing, and it's numbers attribute set to [1, 3, 3, 7]. And that would be the case server side only.

Currently, the clients will not be sent these values, and will think the Thing has an empty array. Thus their states are out of sync.

A work around exists by doing this instead:

const thing = manager.create.thing();
thing.numbers.push(1, 3, 3, 7);

So obviously, something is broken in the game object factory and not registering deltas. Probably around the Delta Array stuff, and probably also around using any too much.

Chess FEN String GameSetting

Players should be able to send FEN string requesting a game start in a specific state.

./run Chess --gameSettings "fen=some_fen_list_of_moves_string"

Chess.js natively supports FEN strings, but our framework would need to parse them. It should be easy, just read each move and pipe them as if we are pre-playing a ton of moves.

chess SAN castling notation with zeroes '0-0' is unsupported

Currently the server will claim 0-0 and 0-0-0 (using zeroes) are illegal moves for designating castling.
both zeroes '0' and the capital letter 'O' should be acceptable ways to designate castling SAN notation.

The implementation could be as simple as replacing '0-0' and '0-0-0' (zeroes) with 'O-O' and 'O-O-O' on the server side before continuing with validation logic.

I expect AI implementers to google how to implement SAN notation, and the top links for 'chess SAN' either only mention using zeroes, or only mention the alternative of using capital letter 'O' as a sidenote:

https://en.wikipedia.org/wiki/Algebraic_notation_(chess)#Castling
Castling is indicated by the special notations 0-0 (for kingside castling) and 0-0-0 (queenside castling).

While the FIDE Handbook, appendix C.13[5] uses the digit zero (0-0 and 0-0-0), PGN requires the uppercase letter O (O-O and O-O-O).

   +-----------------+
 8 | r n b q k . . r |
 7 | p p p p b p p p |
 6 | . . . . p n . . |
 5 | . . . . . . . . |
 4 | . . . . . . . . |
 3 | . . . P . N P B |
 2 | P P P . P P . P |
 1 | R N B Q K . . R |
   +-----------------+
     a b c d e f g h
Game is over. I lost :( because: Made an invalid move ("0-0").

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.