j4nk 0323c86399 | ||
---|---|---|
.gitignore | ||
LICENSE | ||
Makefile | ||
README.md | ||
ball.txt | ||
card.txt | ||
demo2.lua | ||
incorrect_oct_init.lua | ||
incorrect_oct_loop.lua | ||
incorrect_script.lua | ||
json.lua | ||
lobby.lua | ||
main.c | ||
oct_log.c | ||
oct_log.h | ||
oct_networking.c | ||
oct_networking.h | ||
oct_termbox_sprite.c | ||
oct_termbox_sprite.h | ||
oct_utils.lua | ||
paddle.txt | ||
pong.lua | ||
termbox.h | ||
termbox_defs.lua | ||
termbox_render.h | ||
test.c | ||
test_client.lua | ||
test_lobby_server.lua | ||
test_server.lua |
README.md
Open Card Table
A text-based tabletop game engine written in C and scripted with Lua
Motivation
I originally came up with this idea for the purpose of understanding the interaction between a rendering engine, game logic, and a scripting language along with learning how to use the C-Lua interface, learning the Unix sockets API, and experiencing firsthand manual memory management on a decently-sized project. Additionally, this project fills a gap I perceive in the open source gaming community; that is, a way to play tabletop games with friends on basically any *NIX system with a CPU. Indeed, this project runs only in the terminal; as of yet there are no graphical capabilities. It should be noted that the project has only been compiled on Linux, once the game is complete I will add in the necessary code for building on MacOS, OpenBSD, and FreeBSD.
Targets
Currently, the only target is Linux. After the game is finished, it will target Mac OSX, OpenBSD, and FreeBSD. Other targets may be added after that. Windows support is not planned, however it may be possible to run this in WSL, I have no idea how well WSL works with the Unix sockets API.
Dependencies
Currently, the only dependencies are termbox2 and Lua 5.3. An older version of termbox2 is included in the repository for convenience. I used termbox2 instead of ncurses because ncurses leaks memory, and one of my goals for this project is to not leak memory at all. Also, termbox2 is much, much simpler than ncurses in my opinion. I plan on upgrading to Lua 5.4 eventually, when I began this project Lua 5.4 was already out but a tutorial I was following was using Lua 5.3.
License
This project is released under the MIT license. This means that you can basically do anything you want with it, including forking the code and releasing that under a nonfree license.
Building
cd
to the directory and do make
. This will almost certainly not work on a non-Linux system right now.
Usage
open_card_table [options] LUA_FILE
LUA_FILE is the file to run
Options may be
-p PORT
: Port to listen on for connections-v
: Prints version-h
: Prints help-ll LOG_LEVEL
: Sets the log level for output: 0=error, 1=warning, 2=information, 3=debug-lf LOG_FILE
: Sets the output file for the log,stderr
by default
Provided Lua scripts
The lua scripts in the repository are provided as examples and sanity checks
- demo2.lua - I don't actually remember what this was for... some testing in the early stages of development
- incorrect_oct_init.lua - A script that has a runtime error in the
oct_init
function, used for testing lua errors and memory leaks - incorrect_oct_loop.lua - A script that has a runtime error in the
oct_loop
function, used for testing lua errors and memory leaks - incorrect_script.lua - A script that will fail to parse successfully, used for testing lua errors and memory leaks
- oct_utils.lua - Defines useful constants and functions specific to Open Card Table
- pong.lua - Implements 2 player pong, with both players using the same keyboard on the same computer. Does not work very well because of n-key rollover
- termbox_defs.lua - Contains termbox2-specific stuff needed in Lua, mainly key constants defined by termbox2
- test_client/test_server.lua - Scripts used for testing networking. Server should listen on port
1234
and client can listen on any port; both should be on same computer. Client sends message to server every second or so and the server echoes what it received
Developing Lua scripts
The best way to learn scripting for Open Card Table is to look at the included Lua scripts, especially pong.lua as that's the only complete project as of right now. Unfortunately, pong.lua does not demonstrate networking at all but I am currently working on some projects to do this.
Includes
It is generally a good idea to include oct_utils.lua
and termbox_defs.lua
at the top of every script. This can be accomplished with the following lines
require("oct_utils");
require("termbox_defs");
Of course, this requires the script under development be in the same directory as these files.
Required functions
Every Open Card Table lua script must have 2 functions defined: oct_init
and oct_loop
. oct_init
is called once at the beginning of program execution and should be used for initializing variables, initializing the environment, defining sprites, etc. oct_loop
is called once per iteration of the main loop in the C part of the code, and is where all game logic should reside. open_card_table
will exit with an error if either one of these is missing.
oct_init
oct_init
needs to return two variables in order: a variable defining whether or not we need networking, and a variable defining whether or not we need termbox. oct_utils.lua
defines some handy, self-explanatory constants for this: OCT_NOT_NEEDS_NETWORKING
, OCT_NEEDS_NETWORKING
, OCT_NOT_NEEDS_TERMBOX
, and OCT_NEEDS_TERMBOX
. Singleplayer games obviously do not need networking, and generally the only time you will not want termbox is when you are running a server program as opposed to a client.
oct_loop
This function is where the game logic resides. It generally consists of reading in keyboard input and adjusting sprites accordingly. It does not return anything.
Sprites
Sprites are the basic unit or what's drawn on screen. They should be declared in global scope, e.g. like
paddle_left = oct_tb_sprite_new();
Sprites are managed on the C side of things, and oct_tb_sprite_new
allocates memory for each sprite; all sprite references are stored in a dynamically growing array so the limit of how many sprites you may have is determined only by your hardware. Sprites can only be created, not deleted. Every sprite has 3 required attributes: x
, y
, and shape
; other attributes may be defined for game logic but these three are reserved. x
and y
are just the location of the top-left corner of the sprite on screen. shape
is the representation of the sprite. This can be manually specified as a string, or sprites can be defined in a file and read in via the function load_termbox_sprite
. Here is an example:
function oct_init()
...
paddle_left["shape"] = load_termbox_sprite("paddle.txt");
paddle_left["x"] = 1;
paddle_left["y"] = math.floor(height/2)-2;
...
end
Networking
Note that at this time, networking is incomplete and only basic functionality is working. Networking consists of the oct_recv
and oct_send
functions. Note that in order to use networking, oct_init()
had to return OCT_NEEDS_NETWORKING
as the first argument, or else nothing will happen. These usually are used in the oct_loop
function. I still need to add in support for broadcasting, this will come in a while. Important: Message sending is synchronous, i.e. under the hood a message and destination are placed in buffers, and at the next invocation of the main loop the message is sent to the destination. I believe that tabletop games are simple enough that this is sufficient to express games even with real-time components as the main loop is executed at a high frequency. If this turns out not to be the case, I will look into adding asynchronous message sending.
Networking examples
Receiving a message: msg,addr,port = oct_recv();
Sending a message: oct_send("MESSAGE", "IP_ADDRESS", "PORT");
Logging
I have written a convenient logging interface for Lua, it uses the logging macros I implemented in C. It can be invoked in Lua via the OCT_LOG_ERROR
, OCT_LOG_INFO
, OCT_LOG_WARNING
, and OCT_LOG_DEBUG
functions, passing the desired string as an argument. Remember that termbox2 takes over the console so it is a good idea to use the -lf
switch to specify an output file if you are running a termbox-enabled program. Also remember that the log level needs to be set to a sufficient strength via -ll
in order to see the messages.