Compare commits
45 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ae8a3bf0c2 | ||
|
f13a8bd5c4 | ||
|
5e66596d30 | ||
|
3bb5766061 | ||
|
96153f8c60 | ||
|
96f5c08c14 | ||
|
d65ef46634 | ||
|
06057e6e52 | ||
|
1e38a5f107 | ||
|
0ead12c7dd | ||
|
1b2010faba | ||
|
30af43ae95 | ||
|
7811daa78b | ||
|
c51119c3e2 | ||
25e0fa04de | |||
3291bf126e | |||
|
a0ff8680a3 | ||
|
a7421fa07b | ||
|
1c97177956 | ||
|
3ed25cd37f | ||
|
b141314434 | ||
|
5c8666d4fb | ||
|
43d688a728 | ||
|
4055d85d99 | ||
|
1785ce2fc0 | ||
|
61c012370d | ||
|
b532f1c9c8 | ||
|
18cfcff264 | ||
|
b34b933dcd | ||
|
b43927a1da | ||
|
770502184c | ||
|
bccd043d2c | ||
|
a81b4ff8d0 | ||
|
e01a718ac6 | ||
|
56e767bb5b | ||
|
4285770d52 | ||
|
dcb9d68fb4 | ||
|
83b9990bcc | ||
|
b4240cf1c8 | ||
|
0619031cb6 | ||
|
2281bcb6cd | ||
|
8835bfb82a | ||
|
0962546a82 | ||
|
3b5599ffad | ||
|
ca5526b2b7 |
32 changed files with 1193 additions and 436 deletions
|
@ -2,8 +2,18 @@ cmake_minimum_required( VERSION 2.8.7 )
|
|||
|
||||
# Set version information
|
||||
set( TTRTS_VERSION_MAJOR 0 )
|
||||
set( TTRTS_VERSION_MINOR 3 )
|
||||
set( TTRTS_VERSION_PATCH 2 )
|
||||
set( TTRTS_VERSION_MINOR 4 )
|
||||
set( TTRTS_VERSION_PATCH 0 )
|
||||
|
||||
# Set defaults for ttrts variables
|
||||
set( TTRTS_MAPS "/usr/local/share/ttrts/maps/" )
|
||||
set( TTRTS_GAMES "/tmp/" )
|
||||
set( TTRTS_PORT 11715 )
|
||||
|
||||
# define these defaults in code
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_MAPS=${TTRTS_MAPS}" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_GAMES=${TTRTS_GAMES}" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_PORT=${TTRTS_PORT}" )
|
||||
|
||||
# Use c++1y (14)
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++1y" )
|
||||
|
@ -26,7 +36,7 @@ install( FILES scripts/ttrts_complete DESTINATION /etc/bash_completion.d/ )
|
|||
# Run the gen_usage script to generate our usage header
|
||||
add_custom_target(
|
||||
ttrts-gen-manpage ALL
|
||||
${CMAKE_SOURCE_DIR}/scripts/gen_manpage.sh "${TTRTS_VERSION_MAJOR}" "${TTRTS_VERSION_MINOR}" "${TTRTS_VERSION_PATCH}" "ttrts.6" "${CMAKE_SOURCE_DIR}/source/client/README.md"
|
||||
${CMAKE_SOURCE_DIR}/scripts/gen_manpage.sh "${TTRTS_VERSION_MAJOR}" "${TTRTS_VERSION_MINOR}" "${TTRTS_VERSION_PATCH}" "ttrts.6" "${CMAKE_SOURCE_DIR}/source/README.md"
|
||||
)
|
||||
|
||||
# Install the ttrts man page
|
||||
|
@ -41,9 +51,4 @@ endif()
|
|||
install( FILES "${CMAKE_BINARY_DIR}/ttrts.6" DESTINATION ${MANPAGE_LOC} )
|
||||
|
||||
# Subprojects
|
||||
add_subdirectory( source/ttrts )
|
||||
add_subdirectory( source/client )
|
||||
|
||||
# Auxhilary binaries
|
||||
add_subdirectory( source/test )
|
||||
add_subdirectory( source/gen )
|
||||
add_subdirectory( source )
|
||||
|
|
|
@ -31,6 +31,14 @@ TTRTS is from the ground up designed to be a fun way to practice programming. An
|
|||
-----------------------------------------------------------
|
||||
## Changelog
|
||||
|
||||
#### v0.4.0
|
||||
* Updated with network functionality
|
||||
* Game can now be hosted with ttrts --server option
|
||||
* Server can be connected to with ttrts --client
|
||||
* Updated command line interface with new launcher script
|
||||
* map file must now be specified with --map=FILE
|
||||
* Slight refactor of libraries to account for new run targets
|
||||
|
||||
#### v0.3.2
|
||||
* Fix bug when loading map files with walls
|
||||
* Fix ttrts on OSX
|
||||
|
|
|
@ -238,6 +238,13 @@ sub PrintGameFromGamestateString
|
|||
$map[$invalidPos[0]][$invalidPos[1]] = "~";
|
||||
}
|
||||
|
||||
# Fill with walls
|
||||
foreach my $wall ( $info[8] =~ /\[(\d+,\d+)\]/g )
|
||||
{
|
||||
$wall =~ /(\d+),(\d+)/;
|
||||
$map[$1][$2] = "|";
|
||||
}
|
||||
|
||||
# Fill with units
|
||||
for my $unit (@units)
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ if [[ $? != 0 ]]; then
|
|||
exit
|
||||
fi
|
||||
|
||||
echo "Performing install"
|
||||
echo "TTRTS: Performing install"
|
||||
sudo make install
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "TTRTS: Install failed, check output"
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#! /bin/bash
|
||||
# Used to a man page from markdown
|
||||
|
||||
echo ".\" Man page for the ttrts project" > $4
|
||||
echo ".\" this man page is auto-generated, do not edit directly" >> $4
|
||||
FILE="$4"
|
||||
TEMP="$FILE.tmp"
|
||||
|
||||
echo ".TH TTRTS\ v$1.$2.$3 6 $(date +%Y-%m-%d) http://mdiluz.github.io/ttrts/" >> $4
|
||||
echo ".\" Man page for the ttrts project" > $TEMP
|
||||
echo ".\" this man page is auto-generated, do not edit directly" >> $TEMP
|
||||
|
||||
echo ".TH TTRTS\ v$1.$2.$3 6 $(date +%Y-%m-%d) http://mdiluz.github.io/ttrts/" >> $TEMP
|
||||
|
||||
# NOTE: For the OSX version of sed we use -E, which on linux appears be an undocumented switch for -r
|
||||
# we also have to use [A-Za-z] instead of \w for some reason
|
||||
|
@ -32,4 +35,10 @@ cat "$5" \
|
|||
| sed -E 's/-----+//g' \
|
||||
| sed -E 's/`(.*)`/\\fB\1\\fR/g' \
|
||||
| sed -E 's/MAPFILE/\\fImapfile\\fR/g' \
|
||||
| sed -E 's/ ttrts -/ ttrts \\-/g' >> $4
|
||||
| sed -E 's/HOSTNAME/\\fIhostname\\fR/g' \
|
||||
| sed -E 's/ ttrts -/ ttrts \\-/g' >> $TEMP
|
||||
|
||||
|
||||
if [ ! -e $FILE ] || [ ! -z "$( diff $FILE $TEMP )" ]; then
|
||||
mv -f $TEMP $FILE
|
||||
fi
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
#! /bin/bash
|
||||
# Used to generate usage text from markdown
|
||||
|
||||
FILE="$1"
|
||||
TEMP="${FILE}_tmp"
|
||||
|
||||
cat README.md \
|
||||
| sed -E 's/^#+ //g' \
|
||||
| sed -E 's/^ /\\t/g' \
|
||||
| sed -E 's/^ /\\t/g' \
|
||||
| sed -E 's/^/\"/' \
|
||||
| sed -E 's/$/\\n\"/' \
|
||||
> $1
|
||||
> $TEMP
|
||||
|
||||
# If no difference
|
||||
if [ ! -e $FILE ] || [ ! -z "$( diff $TEMP $FILE )" ]; then
|
||||
mv -f $TEMP $FILE
|
||||
fi
|
||||
|
|
|
@ -10,4 +10,9 @@ HEADER="// Auto generated ttrts version header
|
|||
|
||||
#endif //_TTRTS_VERSION_H_"
|
||||
|
||||
echo "$HEADER" > "version.h"
|
||||
echo "$HEADER" > "version.h.tmp"
|
||||
|
||||
# If no difference
|
||||
if [ ! -e version.h ] || [ ! -z "$( diff version.h version.h.tmp )" ]; then
|
||||
mv -f version.h.tmp version.h
|
||||
fi
|
||||
|
|
|
@ -5,11 +5,11 @@ test ! -z TTRTS_MAPS && TTRTS_MAPS=/usr/share/ttrts/maps/
|
|||
have ttrts &&
|
||||
function _ttrts
|
||||
{
|
||||
commandnames=""
|
||||
commandnames="--server --client --host= --host=localhost "
|
||||
for filename in ${TTRTS_MAPS}/*
|
||||
do
|
||||
map="${filename##*/}"
|
||||
commandnames+="$map "
|
||||
commandnames+="--map=$map "
|
||||
done
|
||||
|
||||
local cur prev
|
||||
|
|
13
source/CMakeLists.txt
Normal file
13
source/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Main libraries
|
||||
add_subdirectory( ttrts )
|
||||
add_subdirectory( system )
|
||||
|
||||
# Main binaries
|
||||
add_subdirectory( launcher )
|
||||
add_subdirectory( client )
|
||||
add_subdirectory( server )
|
||||
add_subdirectory( local )
|
||||
|
||||
# Auxhilary binaries
|
||||
add_subdirectory( test )
|
||||
add_subdirectory( gen )
|
128
source/README.md
128
source/README.md
|
@ -1,13 +1,121 @@
|
|||
# Targets
|
||||
### client
|
||||
Main TTRTS binary, runs from the command line and acts as host for games
|
||||
# NAME
|
||||
ttrts - Tiny Terminal RTS
|
||||
|
||||
### test
|
||||
Test binary, to be compiled and run to test various functionality
|
||||
# SYNOPSIS
|
||||
ttrts [--server] [--client] [--host=HOSTNAME] [--map=MAPFILE]
|
||||
|
||||
### gen
|
||||
Binary to generate example map files
|
||||
# DESCRIPTION
|
||||
ttrts is a tiny terminal based RTS that uses text files as order lists to control the units
|
||||
|
||||
# Libraries
|
||||
### ttrts
|
||||
Implementation of the RTS rules and simulation
|
||||
This means that any user, program or cat that can read and write to text files can play the game
|
||||
|
||||
# RETURN VALUE
|
||||
ttrts will return -1 on error, or the winning player on completion
|
||||
|
||||
# OPTIONS
|
||||
--server - Run in server mode, must provide a map file
|
||||
|
||||
--client - Run in client mode, must provide a hostname for a running server
|
||||
|
||||
--map=MAPFILE - File to read in the initial game state. Local or in ${TTRTS_MAPS}
|
||||
|
||||
--host=HOSTNAME - Name of host to connect to in client mode
|
||||
|
||||
# USAGE
|
||||
When invoked, ttrts will set up the game in a directory within ${TTRTS_GAMES} by the name of the map
|
||||
|
||||
The files in this directory can be read and interpreted by human, robot or cat
|
||||
|
||||
ttrts will then await order files from each participant
|
||||
|
||||
Once all order files have been received ttrts will calculate the turn and output a new gamestate file
|
||||
|
||||
In server and client mode, the client will output and read in these files while the server simulates the game
|
||||
|
||||
This process repeats until the game is over
|
||||
|
||||
# ENVIRONMENT
|
||||
${TTRTS_MAPS} - Map file lookup location, defaults to `/usr/share/ttrts/maps/`
|
||||
|
||||
${TTRTS_GAMES} - Game directory for I/O, defaults to `/tmp/`
|
||||
|
||||
-----------------------------------------------------------
|
||||
# FILES
|
||||
`/usr/share/ttrts/maps/` holds a sample set of maps
|
||||
|
||||
## Gamestate File
|
||||
Turn_{TURNNUMBER}.txt
|
||||
|
||||
### Contents
|
||||
===== ttrts v{MAJOR}.{MINOR}.{PATCH} =====
|
||||
NAME:{GAMENAME}
|
||||
SIZE:[{X},{Y}]
|
||||
TURN:{TURNNUMBER}
|
||||
WALL:[{X},{Y}][{X},{Y}][{X},{Y}]...{repeat for all walls}
|
||||
~~~~
|
||||
UNIT:{ID} pl:{PLAYER} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]
|
||||
... {continue for all units}
|
||||
END
|
||||
|
||||
## Order File
|
||||
Player_{PLAYER_ID}_Turn_{TURN_NUMBER}.txt
|
||||
|
||||
### Contents
|
||||
ORDER:{ORDER_CHAR} id:{UNIT_ID}
|
||||
... {continue for all orders}
|
||||
END
|
||||
|
||||
-----------------------------------------------------------
|
||||
# SERVER/CLIENT
|
||||
When in server or client mode, the game can be played across a network. If desired, a player could design an AI to act as a client instead of using the client mode and intermediary filesystem.
|
||||
|
||||
## Protocol
|
||||
The server is accesible on port 11715
|
||||
|
||||
To perform the handshake the server will write to the socket with the format "player PLAYER_ID name GAME_NAME", it will expect this exact information to be written back to in reply.
|
||||
|
||||
Once handshake is performed, the server will write to the socket in the form of the Gamestate file as above.
|
||||
|
||||
The server will then wait for a new-line delimited and END terminated list of orders
|
||||
|
||||
This will be repeated until the game is over
|
||||
|
||||
-----------------------------------------------------------
|
||||
# GAMEPLAY
|
||||
|
||||
The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board
|
||||
|
||||
Each turn, the client outputs a gamestate file and waits for an order file from each player
|
||||
|
||||
All commands are evaluated simultaneously with friendly fire enabled by default
|
||||
|
||||
The game is over when any of three conditions are met -
|
||||
* All remaining units are controlled by a single player
|
||||
* No units are left (draw)
|
||||
* All units left are unable to move (draw)
|
||||
|
||||
# UNITS
|
||||
Each unit occupies a single tile on the board, facing in a compass direction (NESW)
|
||||
|
||||
Units will only accept orders from their owner
|
||||
|
||||
Units can receive only a single order each turn
|
||||
|
||||
Units cannot occupy the same tile as other units/walls
|
||||
|
||||
# ORDERS
|
||||
### F - Move unit [F]orward one space, leaving a wall
|
||||
|
||||
This wall will remain until the end of the game, blocking movement to that tile
|
||||
|
||||
Movement orders have no effect if impossible, eg.
|
||||
* Attempting to move outside of map
|
||||
* Attempting to move on to tile occupied by unit/wall
|
||||
|
||||
### L/R - Rotate unit [L]eft or [R]ight
|
||||
|
||||
Unit will rotate clockwise or counter-clockwise, this order cannot fail
|
||||
|
||||
### A - [A]ttack in straight line in front of unit
|
||||
|
||||
Attack will continue forward until unit can't progress, all units within the path of the attack are destroyed.
|
||||
|
|
|
@ -3,40 +3,21 @@
|
|||
project( ttrts-client )
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../maths
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../system
|
||||
../ttrts
|
||||
)
|
||||
|
||||
# Add the sources
|
||||
set( SOURCES
|
||||
main.cpp
|
||||
client.cpp
|
||||
)
|
||||
|
||||
# Set defaults for ttrts maps and games
|
||||
set( TTRTS_MAPS "/usr/local/share/ttrts/maps/" )
|
||||
set( TTRTS_GAMES "/tmp/" )
|
||||
|
||||
# define these defaults in code
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_MAPS=${TTRTS_MAPS}" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTTRTS_GAMES=${TTRTS_GAMES}" )
|
||||
|
||||
# Add the executable
|
||||
add_executable( ${PROJECT_NAME} ${SOURCES} )
|
||||
|
||||
# Set our output name to ttrts
|
||||
set_target_properties( ${PROJECT_NAME} PROPERTIES OUTPUT_NAME ttrts )
|
||||
|
||||
# dependent on main ttrts libary
|
||||
target_link_libraries( ${PROJECT_NAME} ttrts )
|
||||
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system )
|
||||
|
||||
# Installation target
|
||||
install( TARGETS ${PROJECT_NAME} DESTINATION bin )
|
||||
|
||||
# Run the gen_usage script to generate our usage header
|
||||
add_custom_target(
|
||||
ttrts-client-usage
|
||||
cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_SOURCE_DIR}/scripts/gen_usage.sh "${CMAKE_CURRENT_BINARY_DIR}/usage.h"
|
||||
)
|
||||
|
||||
add_dependencies(${PROJECT_NAME} ttrts-client-usage)
|
|
@ -1,98 +0,0 @@
|
|||
# NAME
|
||||
ttrts - Tiny Terminal RTS
|
||||
|
||||
# SYNOPSIS
|
||||
ttrts MAPFILE
|
||||
|
||||
# DESCRIPTION
|
||||
ttrts is a tiny terminal based RTS that uses text files as order lists to control the units
|
||||
|
||||
This means that any user, program or cat that can read and write to text files can play the game
|
||||
|
||||
# RETURN VALUE
|
||||
ttrts will return -1 on error, or the winning player on completion
|
||||
|
||||
# OPTIONS
|
||||
MAPFILE - File to read in the initial game state. Local or in ${TTRTS_MAPS}
|
||||
|
||||
# USAGE
|
||||
When invoked, ttrts will set up the game in a directory within ${TTRTS_GAMES} by the name of the map
|
||||
|
||||
The files in this directory can be read and interpreted by human, robot or cat
|
||||
|
||||
ttrts will then await order files from each participant
|
||||
|
||||
Once all order files have been received ttrts will calculate the turn and output a new gamestate file
|
||||
|
||||
This process repeats until the game is over
|
||||
|
||||
# ENVIRONMENT
|
||||
${TTRTS_MAPS} - Map file lookup location, defaults to `/usr/share/ttrts/maps/`
|
||||
|
||||
${TTRTS_GAMES} - Game directory for I/O, defaults to `/tmp/`
|
||||
|
||||
-----------------------------------------------------------
|
||||
# FILES
|
||||
`/usr/share/ttrts/maps/` holds a sample set of maps
|
||||
|
||||
## Gamestate File
|
||||
Turn_{TURNNUMBER}.txt
|
||||
|
||||
### Contents
|
||||
===== ttrts v{MAJOR}.{MINOR}.{PATCH} =====
|
||||
NAME:{GAMENAME}
|
||||
SIZE:[{X},{Y}]
|
||||
TURN:{TURNNUMBER}
|
||||
WALL:[{X},{Y}][{X},{Y}][{X},{Y}]...{repeat for all walls}
|
||||
~~~~
|
||||
UNIT:{ID} pl:{PLAYER} vs:{VIS} dr:{DIR(NESW)} ps:[{X},{Y}]
|
||||
... {continue for all units}
|
||||
END
|
||||
|
||||
## Order File
|
||||
Player_{PLAYER_ID}_Turn_{TURN_NUMBER}.txt
|
||||
|
||||
### Contents
|
||||
ORDER:{ORDER_CHAR} id:{UNIT_ID}
|
||||
... {continue for all orders}
|
||||
END
|
||||
|
||||
-----------------------------------------------------------
|
||||
# GAMEPLAY
|
||||
|
||||
The game takes place in a series of simultaneous turns on an arbitrarily sized 2D board
|
||||
|
||||
Each turn, the client outputs a gamestate file and waits for an order file from each player
|
||||
|
||||
All commands are evaluated simultaneously with friendly fire enabled by default
|
||||
|
||||
The game is over when any of three conditions are met -
|
||||
* All remaining units are controlled by a single player
|
||||
* No units are left (draw)
|
||||
* All units left are unable to move (draw)
|
||||
|
||||
# UNITS
|
||||
Each unit occupies a single tile on the board, facing in a compass direction (NESW)
|
||||
|
||||
Units will only accept orders from their owner
|
||||
|
||||
Units can receive only a single order each turn
|
||||
|
||||
Units cannot occupy the same tile as other units/walls
|
||||
|
||||
# ORDERS
|
||||
### F - Move unit [F]orward one space, leaving a wall
|
||||
|
||||
This wall will remain until the end of the game, blocking movement to that tile
|
||||
|
||||
Movement orders have no effect if impossible, eg.
|
||||
* Attempting to move outside of map
|
||||
* Attempting to move on to tile occupied by unit/wall
|
||||
|
||||
### L/R - Rotate unit [L]eft or [R]ight
|
||||
|
||||
Unit will rotate clockwise or counter-clockwise, this order cannot fail
|
||||
|
||||
### A - [A]ttack in straight line in front of unit
|
||||
|
||||
Attack will continue forward until unit can't progress, all units within the path of the attack are destroyed.
|
69
source/client/client.cpp
Normal file
69
source/client/client.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "net.h"
|
||||
#include "game.h"
|
||||
#include "error.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// must provide information
|
||||
if (argc < 2)
|
||||
fatal_error("Usage: ttrts-client HOST");
|
||||
|
||||
std::string hostname = argv[1];
|
||||
|
||||
sockaddr_in serv_addr; // Server address
|
||||
memset(&serv_addr,0, sizeof(serv_addr));
|
||||
|
||||
// Set the server to AF_INET
|
||||
serv_addr.sin_family = AF_INET;
|
||||
// Set our server address port to the port number provided
|
||||
serv_addr.sin_port = htons(TTRTS_PORT);
|
||||
|
||||
std::cout<<"TTRTS: Connecting to "<<hostname<<std::endl;
|
||||
int sockfd = ConnectToHostServer(hostname, serv_addr);
|
||||
|
||||
unsigned int player;
|
||||
std::string gameNameString;
|
||||
|
||||
// Handshake with server to fetch player and gamestring
|
||||
PerformClientHandshake(sockfd, player, gameNameString);
|
||||
|
||||
// output our information
|
||||
player_t myPlayer = (player_t)player;
|
||||
std::cout<<"TTRTS: I am player "<<std::to_string((int)myPlayer)<<std::endl;
|
||||
std::cout<<"TTRTS: Game is "<<gameNameString<<std::endl;
|
||||
|
||||
// Clean out the games dir
|
||||
CreateAndCleanGameDir(gameNameString);
|
||||
|
||||
// Buffer for messages
|
||||
char buffer[1028];
|
||||
memset(buffer,0,sizeof(buffer));
|
||||
|
||||
int n = 0; // return value for read and write calls
|
||||
while ( n >= 0 )
|
||||
{
|
||||
std::cout<<"TTRTS: Waiting for gamestate"<<std::endl;
|
||||
std::string gamestate = WaitForGamestateMessage(sockfd);
|
||||
|
||||
// Output the gamestate file for this game
|
||||
CTTRTSGame thisGame = GetGameFromString(gamestate);
|
||||
OutputGameStateFile(thisGame);
|
||||
|
||||
// If game over, exit with out winning player and message
|
||||
if(thisGame.GameOver())
|
||||
exit( OutputGameEnd(thisGame) );
|
||||
|
||||
// Get the order file for this turn`
|
||||
std::string orders = GetOrdersFromPlayerFile(thisGame,myPlayer);
|
||||
|
||||
std::cout<<"TTRTS: Sending orders"<<std::endl;
|
||||
std::cout<<orders<<std::endl;
|
||||
// Write to the socket with the buffer
|
||||
n = SendOrdersToServer(sockfd, orders);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <formatters.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "game.h"
|
||||
|
||||
#define STRINGIFY(x) _STRINGIFY(x)
|
||||
#define _STRINGIFY(x) #x
|
||||
|
||||
static const char* sk_usage =
|
||||
#include "usage.h"
|
||||
;
|
||||
|
||||
// Verbose mode
|
||||
static const bool env_verbose = getenv("VERBOSE");
|
||||
|
||||
// time for waiting between file stats
|
||||
static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100);
|
||||
|
||||
// Check if a file exists
|
||||
inline bool FileExists( const std::string& name )
|
||||
{
|
||||
struct stat buffer;
|
||||
return (stat (name.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
// Wait for a file to exist
|
||||
inline void WaitForFile( const std::string& name, const std::chrono::milliseconds& time )
|
||||
{
|
||||
while( !FileExists(name) ) std::this_thread::sleep_for(time);
|
||||
}
|
||||
|
||||
bool OutputGameStateFile(CTTRTSGame &game, std::string &gameDir)
|
||||
{
|
||||
char turnFileName[128];
|
||||
snprintf(turnFileName,128,"%s/Turn_%i.txt",gameDir.c_str(),game.GetTurn());
|
||||
std::ofstream turnFile(turnFileName, std::ios_base::trunc); // truncate to overwrite if a file exists
|
||||
|
||||
if ( turnFile.bad() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Output the turn description
|
||||
std::string turnDescriptor = GetStringFromGame(game);
|
||||
|
||||
turnFile<<turnDescriptor;
|
||||
turnFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Main program entry point
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// If no args, print usage
|
||||
if ( argc == 1 )
|
||||
{
|
||||
std::cout<<sk_usage<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Attempt to open the game file
|
||||
std::string gameFile = argv[1];
|
||||
|
||||
// Default for maps
|
||||
std::string ttrts_maps_dir = STRINGIFY(TTRTS_MAPS);
|
||||
if( getenv("TTRTS_MAPS") )
|
||||
{
|
||||
ttrts_maps_dir = getenv("TTRTS_MAPS");
|
||||
|
||||
// Additional trailing slash
|
||||
if( ttrts_maps_dir.back() != '/' )
|
||||
ttrts_maps_dir += "/";
|
||||
}
|
||||
|
||||
// Default for games
|
||||
std::string ttrts_games_dir = STRINGIFY(TTRTS_GAMES);
|
||||
if( getenv("TTRTS_GAMES") )
|
||||
{
|
||||
ttrts_games_dir = getenv("TTRTS_GAMES");
|
||||
|
||||
// Additional trailing slash
|
||||
if( ttrts_games_dir.back() != '/' )
|
||||
ttrts_games_dir += "/";
|
||||
}
|
||||
|
||||
// If file path is not local path and file doesn't exist
|
||||
if( gameFile.find("/") == std::string::npos
|
||||
&& access( gameFile.c_str(), F_OK ) == -1 )
|
||||
{
|
||||
gameFile = ttrts_maps_dir + gameFile;
|
||||
}
|
||||
|
||||
// If still not good
|
||||
if( access( gameFile.c_str(), F_OK ) == -1 )
|
||||
{
|
||||
std::cerr<<"Error: "<<gameFile<<" file not found"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::ifstream file(gameFile);
|
||||
|
||||
std::cout<<"Launching TTRTS with "<<gameFile<<std::endl;
|
||||
|
||||
std::string gameDescriptor;
|
||||
|
||||
// Reserve the string needed up front
|
||||
file.seekg(0, std::ios::end);
|
||||
gameDescriptor.reserve(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Grab the string from the file
|
||||
gameDescriptor.assign((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
|
||||
|
||||
if( gameDescriptor.size() == 0 )
|
||||
{
|
||||
std::cerr<<"Error: failed to read in any information from "<<gameFile<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the game
|
||||
CTTRTSGame game = GetGameFromString(gameDescriptor);
|
||||
|
||||
// Grab the players involved
|
||||
auto players = game.GetPlayers();
|
||||
|
||||
// Current game directory
|
||||
std::string gameDir = ttrts_games_dir + game.GetName();
|
||||
|
||||
// Empty the current game directory
|
||||
struct stat info;
|
||||
int ret = stat( gameDir.c_str(), &info );
|
||||
if( ret == 0 && info.st_mode & S_IFDIR )
|
||||
{
|
||||
std::cout<< gameDir << " game directory already exists"<<std::endl;
|
||||
std::cout<<"Confirm to delete contents [y/N] ";
|
||||
std::string input;
|
||||
std::cin>>input;
|
||||
if( !input.size() || std::tolower(input[0]) != 'y' )
|
||||
{
|
||||
std::cerr<<"Aborting..."<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if ( ret == 0 )
|
||||
{
|
||||
std::cerr<< gameDir << " exists but is not directory \nAborting..."<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the game directory
|
||||
char cmd2[128];
|
||||
snprintf(cmd2,128, "test -d %s || mkdir %s",gameDir.c_str(),gameDir.c_str());
|
||||
if( system(cmd2) == -1)
|
||||
{
|
||||
std::cerr<<"Error: Failed to create the game directory"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Clean out the game directory
|
||||
char cmd1[128];
|
||||
snprintf(cmd1,128, "rm -rf %s/*",gameDir.c_str());
|
||||
if ( system(cmd1) == -1 )
|
||||
{
|
||||
std::cerr<<"Error: Failed to clean the game directory"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// While the game isn't finished
|
||||
while ( ! game.GameOver() )
|
||||
{
|
||||
std::cout<<"Starting turn "<<game.GetTurn()<<std::endl;
|
||||
|
||||
// Create a turn file
|
||||
if( !OutputGameStateFile(game, gameDir))
|
||||
{
|
||||
std::cerr<<"Error: Failed to output new turn file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Wait for order files
|
||||
for( player_t player : players)
|
||||
{
|
||||
// Construct the player order filename
|
||||
char playerOrderFileName[128];
|
||||
snprintf(playerOrderFileName, 128, "%s/Player_%i_Turn_%i.txt", gameDir.c_str(), (int) player, game.GetTurn());
|
||||
|
||||
// Wait for the player order file to be created
|
||||
std::cout<<"Waiting for "<< playerOrderFileName <<std::endl;
|
||||
bool hasOrderFile = false;
|
||||
while(!hasOrderFile)
|
||||
{
|
||||
WaitForFile(playerOrderFileName,sk_waitTime); // Wait for the file
|
||||
|
||||
// File must have END
|
||||
// Method taken from http://stackoverflow.com/questions/11876290/c-fastest-way-to-read-only-last-line-of-text-file
|
||||
std::ifstream turnFile(playerOrderFileName);
|
||||
turnFile.seekg(-1,std::ios_base::end);
|
||||
|
||||
// Loop back from the end of file
|
||||
bool keepLooping = true;
|
||||
while(keepLooping) {
|
||||
char ch;
|
||||
turnFile.get(ch); // Get current byte's data
|
||||
|
||||
if((int)turnFile.tellg() <= 1) { // If the data was at or before the 0th byte
|
||||
turnFile.seekg(0); // The first line is the last line
|
||||
keepLooping = false; // So stop there
|
||||
}
|
||||
else if(ch == '\n') { // If the data was a newline
|
||||
keepLooping = false; // Stop at the current position.
|
||||
}
|
||||
else { // If the data was neither a newline nor at the 0 byte
|
||||
turnFile.seekg(-2,std::ios_base::cur); // Move to the front of that data, then to the front of the data before it
|
||||
}
|
||||
}
|
||||
|
||||
// Grab this line
|
||||
std::string lastLine;
|
||||
std::getline(turnFile,lastLine);
|
||||
if(lastLine == "END")
|
||||
hasOrderFile = true;
|
||||
}
|
||||
|
||||
std::ifstream turnFile(playerOrderFileName);
|
||||
|
||||
// Reserve the full order string
|
||||
std::string orders;
|
||||
turnFile.seekg(0, std::ios::end);
|
||||
orders.reserve(turnFile.tellg());
|
||||
turnFile.seekg(0, std::ios::beg);
|
||||
|
||||
// Grab the string from the file
|
||||
orders.assign((std::istreambuf_iterator<char>(turnFile)),std::istreambuf_iterator<char>());
|
||||
|
||||
// Issue the orders to the game
|
||||
if( game.IssueOrders(player, orders) )
|
||||
std::cerr<<"Warning: Orders for player "<<(int) player <<" failed to correctly parse"<<std::endl;
|
||||
}
|
||||
|
||||
// Simulate turn
|
||||
std::cout<<"Simulating this turn!"<<std::endl;
|
||||
if ( game.SimulateToNextTurn() )
|
||||
{
|
||||
std::cerr << "Error: Failed to simulate for turn "<<game.GetTurn()<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Output final gamestate
|
||||
OutputGameStateFile(game, gameDir);
|
||||
|
||||
// Get the winning player
|
||||
player_t winningPlayer = game.GetWinningPlayer();
|
||||
|
||||
// Print the winner!
|
||||
if ( winningPlayer != player_t::NUM_INVALID )
|
||||
{
|
||||
std::cout<<"Game over! Winner:"<<(int) winningPlayer <<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout<<"Game over! It was a draw!"<<std::endl;
|
||||
}
|
||||
|
||||
return (int)winningPlayer;
|
||||
};
|
|
@ -3,8 +3,7 @@
|
|||
project( ttrts-gen )
|
||||
|
||||
include_directories(
|
||||
../ttrts
|
||||
../maths
|
||||
../ttrts
|
||||
)
|
||||
|
||||
set( SOURCES
|
||||
|
|
|
@ -33,7 +33,7 @@ int main()
|
|||
//------
|
||||
{
|
||||
CTTRTSGame game(6, 6);
|
||||
game.SetName("Tiny2player");
|
||||
game.SetName("Tiny2Player");
|
||||
|
||||
AddUnitToGame( player_t::Red, '<', uvector2(4, 2), game);
|
||||
AddUnitToGame( player_t::Red, '<', uvector2(4, 4), game);
|
||||
|
|
6
source/launcher/CMakeLists.txt
Normal file
6
source/launcher/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# ====================== ttrts =======================
|
||||
# Project name
|
||||
project( ttrts-perl-launch )
|
||||
|
||||
# Add bash completion to install
|
||||
install( PROGRAMS ttrts.pl DESTINATION bin RENAME ttrts )
|
44
source/launcher/ttrts.pl
Executable file
44
source/launcher/ttrts.pl
Executable file
|
@ -0,0 +1,44 @@
|
|||
#! /usr/bin/perl
|
||||
# Main ttrts launcher script
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use 5.0;
|
||||
|
||||
use Getopt::Long qw(GetOptions);
|
||||
|
||||
sub print_usage
|
||||
{
|
||||
print "Unknown option: @_\n" if ( @_ );
|
||||
print "Usage: ttrts [--server] [--client] [--host=HOSTNAME] [--map=MAPFILE]\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
our $VERBOSE = $ENV{"VERBOSE"};
|
||||
|
||||
our $server;
|
||||
our $client;
|
||||
our $host;
|
||||
our $map;
|
||||
|
||||
print_usage() if ( @ARGV < 1 or
|
||||
!GetOptions(
|
||||
'client' => \$client,
|
||||
'server' => \$server,
|
||||
'host=s' => \$host,
|
||||
'map=s' => \$map,
|
||||
) );
|
||||
|
||||
# Verify we have the right parameters
|
||||
print "Cannot run as both client and server\n" and exit if $client and $server;
|
||||
print "Client requires hostname\n" and exit if $client and not $host;
|
||||
print "Server requires mapfile\n" and exit if $server and not $map;
|
||||
print "Running locally requires mapfile\n" and exit if not $server and not $client and not $map;
|
||||
|
||||
# Run client, server or local
|
||||
my $res = -1;
|
||||
$res = system("ttrts-client $host") if $client and $host;
|
||||
$res = system("ttrts-server $map") if $server and $map;
|
||||
$res = system("ttrts-local $map") if not $server and not $client and $map;
|
||||
|
||||
return $res
|
23
source/local/CMakeLists.txt
Normal file
23
source/local/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
# ====================== ttrts =======================
|
||||
# Project name
|
||||
project( ttrts-local )
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../system
|
||||
../ttrts
|
||||
)
|
||||
|
||||
# Add the sources
|
||||
set( SOURCES
|
||||
local.cpp
|
||||
)
|
||||
|
||||
# Add the executable
|
||||
add_executable( ${PROJECT_NAME} ${SOURCES} )
|
||||
|
||||
# dependent on main ttrts libary
|
||||
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system pthread )
|
||||
|
||||
# Installation target
|
||||
install( TARGETS ${PROJECT_NAME} DESTINATION bin )
|
62
source/local/local.cpp
Normal file
62
source/local/local.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "game.h"
|
||||
#include "filesystem.h"
|
||||
#include "error.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// =====================================================================================================================
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// must provide information
|
||||
if (argc < 2)
|
||||
fatal_error("Usage: ttrts-local MAPFILE");
|
||||
|
||||
std::string gamefile = argv[1];
|
||||
|
||||
std::cout<<"TTRTS: Launching with "<<gamefile<<std::endl;
|
||||
CTTRTSGame game = GetGameFromFile(gamefile);
|
||||
|
||||
// Grab the players involved
|
||||
auto players = game.GetPlayers();
|
||||
|
||||
// Default for games
|
||||
std::string ttrts_games_dir = getGamesDir();
|
||||
|
||||
// Empty the current game directory
|
||||
if ( CreateAndCleanGameDir(game.GetName()) < 0)
|
||||
return -1;
|
||||
|
||||
// While the game isn't finished
|
||||
while ( ! game.GameOver() )
|
||||
{
|
||||
std::cout<<"TTRTS: Starting turn "<<game.GetTurn()<<std::endl;
|
||||
|
||||
// Create a turn file
|
||||
if( !OutputGameStateFile(game))
|
||||
fatal_error("Error: Failed to output new turn file");
|
||||
|
||||
// Wait for order files
|
||||
for( player_t player : players)
|
||||
{
|
||||
// Construct the player order filename
|
||||
std::string orders = GetOrdersFromPlayerFile(game, player);
|
||||
|
||||
// Issue the orders to the game
|
||||
if( game.IssueOrders(player, orders) )
|
||||
std::cerr<<"Warning: Orders for player "<<(int) player <<" failed to correctly parse"<<std::endl;
|
||||
}
|
||||
|
||||
// Simulate turn
|
||||
std::cout<<"TTRTS: Simulating this turn!"<<std::endl;
|
||||
if ( game.SimulateToNextTurn() )
|
||||
fatal_error("Failed to simulate game turn");
|
||||
|
||||
}
|
||||
|
||||
// Output final gamestate
|
||||
OutputGameStateFile(game);
|
||||
|
||||
return OutputGameEnd( game );
|
||||
}
|
||||
|
23
source/server/CMakeLists.txt
Normal file
23
source/server/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
# ====================== ttrts =======================
|
||||
# Project name
|
||||
project( ttrts-server )
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../system
|
||||
../ttrts
|
||||
)
|
||||
|
||||
# Add the sources
|
||||
set( SOURCES
|
||||
server.cpp
|
||||
)
|
||||
|
||||
# Add the executable
|
||||
add_executable( ${PROJECT_NAME} ${SOURCES} )
|
||||
|
||||
# dependent on main ttrts libary
|
||||
target_link_libraries( ${PROJECT_NAME} ttrts ttrts-system pthread )
|
||||
|
||||
# Installation target
|
||||
install( TARGETS ${PROJECT_NAME} DESTINATION bin )
|
98
source/server/server.cpp
Normal file
98
source/server/server.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include "error.h"
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
void RunServerForGame(CTTRTSGame &game)
|
||||
{
|
||||
std::cout<<"TTRTS: Setting up server"<<std::endl;
|
||||
|
||||
// Server side information
|
||||
sockaddr_in serv_addr = GetLocalServerAddress();
|
||||
|
||||
// socket File descriptor
|
||||
int sockfd = SetUpServerListeningSocket(serv_addr);
|
||||
|
||||
// Get information about the game
|
||||
std::vector<player_t> players = game.GetPlayers();
|
||||
unsigned int numClients = players.size();
|
||||
auto player_iterator = players.begin();
|
||||
|
||||
// game mutex
|
||||
std::mutex gameMutex;
|
||||
|
||||
// Set of clients
|
||||
std::vector<ClientInfo> myClients;
|
||||
|
||||
std::cout<<"TTRTS: Waiting for "<<numClients<<" players"<<std::endl;
|
||||
|
||||
// Loop while we're connecting the clients
|
||||
while ( myClients.size() < numClients )
|
||||
{
|
||||
// information for each client
|
||||
ClientInfo clientInfo;
|
||||
clientInfo.player = *player_iterator;
|
||||
player_iterator++;
|
||||
|
||||
clientInfo = WaitForClientConnection(sockfd, game.GetName(), clientInfo);
|
||||
|
||||
// Add out client info to our list
|
||||
myClients.push_back(clientInfo);
|
||||
|
||||
}
|
||||
|
||||
std::cout<<"TTRTS: All players connected"<< std::endl;
|
||||
|
||||
std::cout<<"TTRTS: Hit enter to begin...";
|
||||
std::cin.ignore();
|
||||
|
||||
// Loop for each turn
|
||||
while ( !game.GameOver() )
|
||||
{
|
||||
// Send data to clients
|
||||
std::cout<<"TTRTS: Broadcasting Gamestate"<< std::endl;
|
||||
SendGamestateToClients(myClients, game, gameMutex);
|
||||
|
||||
// Wait for orders from clients
|
||||
std::cout<<"TTRTS: Waiting for orders from players"<< std::endl;
|
||||
WaitForOrdersFromClients(myClients, game, gameMutex);
|
||||
|
||||
std::cout<<"TTRTS: All orders recieved, simulating turn"<< std::endl;
|
||||
|
||||
// Step to the next turn
|
||||
gameMutex.lock();
|
||||
game.SimulateToNextTurn();
|
||||
gameMutex.unlock();
|
||||
}
|
||||
|
||||
// Send final state to all the clients
|
||||
SendGamestateToClients(myClients, game, gameMutex);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// argv[1] needs to be a valid game file
|
||||
if( argc < 2 )
|
||||
fatal_error("Usage: ttrts-server MAPFILE");
|
||||
|
||||
// Set up game
|
||||
CTTRTSGame game = GetGameFromFile(argv[1]);
|
||||
if(game.GetNumUnits() == 0)
|
||||
fatal_error("game not valid");
|
||||
|
||||
RunServerForGame(game);
|
||||
|
||||
// Return winning player and output game end
|
||||
return OutputGameEnd(game);
|
||||
}
|
21
source/system/CMakeLists.txt
Normal file
21
source/system/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
cmake_minimum_required(VERSION 2.8.7)
|
||||
|
||||
# Main ttrts library
|
||||
project( ttrts-system )
|
||||
|
||||
# Include the maths
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../ttrts
|
||||
)
|
||||
|
||||
# Add our sources
|
||||
set( SOURCES
|
||||
net.cpp
|
||||
filesystem.cpp
|
||||
)
|
||||
|
||||
# Add this library
|
||||
add_library( ${PROJECT_NAME} ${SOURCES} )
|
||||
|
||||
target_link_libraries( ${PROJECT_NAME} ttrts pthread )
|
25
source/system/error.h
Normal file
25
source/system/error.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef _TTRTS_ERROR_H_
|
||||
#define _TTRTS_ERROR_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//======================================================================================================================
|
||||
// Error functions
|
||||
|
||||
// For local fatal errors
|
||||
inline void fatal_error(const char *msg)
|
||||
{
|
||||
std::cerr<<msg<<std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// For system fatal errors (ie. functions that set errno)
|
||||
inline void fatal_perror(const char *msg)
|
||||
{
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
248
source/system/filesystem.cpp
Normal file
248
source/system/filesystem.cpp
Normal file
|
@ -0,0 +1,248 @@
|
|||
#include "filesystem.h"
|
||||
#include "net.h"
|
||||
#include "error.h"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <formatters.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// =====================================================================================================================
|
||||
// time for waiting between file stats
|
||||
static const std::chrono::milliseconds sk_waitTime = std::chrono::milliseconds(100);
|
||||
|
||||
// Check if a file exists
|
||||
bool FileExists( const std::string& name )
|
||||
{
|
||||
struct stat buffer;
|
||||
return (stat (name.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
// Wait for a file to exist
|
||||
void WaitForFile( const std::string& name, const std::chrono::milliseconds& time )
|
||||
{
|
||||
while( !FileExists(name) ) std::this_thread::sleep_for(time);
|
||||
}
|
||||
|
||||
bool OutputGameStateFile(CTTRTSGame &game)
|
||||
{
|
||||
char turnFileName[128];
|
||||
snprintf(turnFileName,128,"%s%s/Turn_%i.txt", getGamesDir().c_str(),game.GetName().c_str(),game.GetTurn());
|
||||
std::ofstream turnFile(turnFileName, std::ios_base::trunc); // truncate to overwrite if a file exists
|
||||
|
||||
if ( turnFile.bad() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Output the turn description
|
||||
std::string turnDescriptor = GetStringFromGame(game);
|
||||
|
||||
turnFile<<turnDescriptor;
|
||||
turnFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getMapsDir()
|
||||
{
|
||||
std::string maps = STRINGIFY(TTRTS_MAPS);
|
||||
if( getenv("TTRTS_MAPS") )
|
||||
{
|
||||
maps = getenv("TTRTS_MAPS");
|
||||
|
||||
// Additional trailing slash
|
||||
if( maps.back() != '/' )
|
||||
maps += "/";
|
||||
}
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
std::string getGamesDir()
|
||||
{
|
||||
std::string dir = STRINGIFY(TTRTS_GAMES);
|
||||
if( getenv("TTRTS_GAMES") )
|
||||
{
|
||||
dir = getenv("TTRTS_GAMES");
|
||||
|
||||
// Additional trailing slash
|
||||
if( dir.back() != '/' )
|
||||
dir += "/";
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
CTTRTSGame GetGameFromFile( const std::string& filename )
|
||||
{
|
||||
std::string gamefile = filename;
|
||||
|
||||
// Default for maps
|
||||
std::string ttrts_maps_dir = getMapsDir();
|
||||
|
||||
// If file path is not local path and file doesn't exist
|
||||
if( gamefile.find("/") == std::string::npos
|
||||
&& access( gamefile.c_str(), F_OK ) == -1 )
|
||||
{
|
||||
gamefile = ttrts_maps_dir + gamefile;
|
||||
}
|
||||
|
||||
// If still not good
|
||||
if( access( gamefile.c_str(), F_OK ) == -1 )
|
||||
fatal_perror("Could not open game file");
|
||||
|
||||
std::ifstream file(gamefile);
|
||||
|
||||
std::string gameDescriptor;
|
||||
|
||||
// Reserve the string needed up front
|
||||
file.seekg(0, std::ios::end);
|
||||
gameDescriptor.reserve(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Grab the string from the file
|
||||
gameDescriptor.assign((std::istreambuf_iterator<char>(file)),std::istreambuf_iterator<char>());
|
||||
|
||||
if( gameDescriptor.size() == 0 )
|
||||
fatal_error("failed to read in any information from gamefile");
|
||||
|
||||
// Create the game
|
||||
return GetGameFromString(gameDescriptor);
|
||||
}
|
||||
|
||||
std::string GetOrdersFromPlayerFile(const CTTRTSGame &game, player_t &player)
|
||||
{
|
||||
std::string gameDir = getGamesDir();
|
||||
|
||||
char playerOrderFileName[128];
|
||||
snprintf(playerOrderFileName, 128, "%s%s/Player_%i_Turn_%i.txt", gameDir.c_str(),game.GetName().c_str(),(int) player, game.GetTurn());
|
||||
|
||||
// Wait for the player order file to be created
|
||||
std::clog<<"TTRTS: Waiting for "<< playerOrderFileName << std::endl;
|
||||
bool hasOrderFile = false;
|
||||
while(!hasOrderFile)
|
||||
{
|
||||
WaitForFile(playerOrderFileName,sk_waitTime); // Wait for the file
|
||||
|
||||
// File must have END
|
||||
// Method taken from http://stackoverflow.com/questions/11876290/c-fastest-way-to-read-only-last-line-of-text-file
|
||||
std::ifstream turnFile(playerOrderFileName);
|
||||
turnFile.seekg(-1, std::ios_base::end);
|
||||
|
||||
// Loop back from the end of file
|
||||
bool keepLooping = true;
|
||||
while(keepLooping) {
|
||||
char ch;
|
||||
turnFile.get(ch); // Get current byte's data
|
||||
|
||||
if((int)turnFile.tellg() <= 1) { // If the data was at or before the 0th byte
|
||||
turnFile.seekg(0); // The first line is the last line
|
||||
keepLooping = false; // So stop there
|
||||
}
|
||||
else if(ch == '\n') { // If the data was a newline
|
||||
keepLooping = false; // Stop at the current position.
|
||||
}
|
||||
else { // If the data was neither a newline nor at the 0 byte
|
||||
turnFile.seekg(-2, std::ios_base::cur); // Move to the front of that data, then to the front of the data before it
|
||||
}
|
||||
}
|
||||
|
||||
// Grab this line
|
||||
std::string lastLine;
|
||||
getline(turnFile,lastLine);
|
||||
if(lastLine == "END")
|
||||
hasOrderFile = true;
|
||||
}
|
||||
|
||||
std::ifstream turnFile(playerOrderFileName);
|
||||
|
||||
// Reserve the full order string
|
||||
std::string orders;
|
||||
turnFile.seekg(0, std::ios_base::end);
|
||||
orders.reserve(turnFile.tellg());
|
||||
turnFile.seekg(0, std::ios_base::beg);
|
||||
|
||||
// Grab the string from the file
|
||||
orders.assign((std::istreambuf_iterator<char>(turnFile)), std::istreambuf_iterator<char>());
|
||||
return orders;
|
||||
}
|
||||
|
||||
int CreateAndCleanGameDir(const std::string& gameName)
|
||||
{
|
||||
std::string gameDir = getGamesDir()+gameName;
|
||||
struct stat info;
|
||||
int ret = stat( gameDir.c_str(), &info );
|
||||
if( ret == 0 && info.st_mode & S_IFDIR )
|
||||
{
|
||||
std::cout<<"TTRTS: " << gameDir << " game directory already exists"<<std::endl;
|
||||
std::cout<<"TTRTS: Confirm to delete contents [y/N] ";
|
||||
std::string input;
|
||||
std::cin>>input;
|
||||
if( !input.size() || std::tolower(input[0]) != 'y' )
|
||||
return -1;
|
||||
}
|
||||
else if ( ret == 0 )
|
||||
{
|
||||
fatal_error("TTRTS_GAMES exists but is not directory \nAborting...");
|
||||
}
|
||||
|
||||
// Create the game directory
|
||||
char cmd2[128];
|
||||
snprintf(cmd2,128, "test -d %s || mkdir %s",gameDir.c_str(),gameDir.c_str());
|
||||
if( system(cmd2) == -1)
|
||||
fatal_error("Error: Failed to create the game directory");
|
||||
|
||||
// Clean out the game directory
|
||||
char cmd1[128];
|
||||
snprintf(cmd1,128, "rm -rf %s/*",gameDir.c_str());
|
||||
if ( system(cmd1) == -1 )
|
||||
fatal_error("Error: Failed to clean the game directory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OutputGameEnd(const CTTRTSGame &game) {
|
||||
std::cout<<"TTRTS: Game Over!"<< std::endl;
|
||||
|
||||
// Get the winning player
|
||||
player_t winningPlayer = game.GetWinningPlayer();
|
||||
|
||||
// Print the winner!
|
||||
if ( winningPlayer != player_t::NUM_INVALID )
|
||||
{
|
||||
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout<<"TTRTS: It was a draw!"<<std::endl;
|
||||
}
|
||||
|
||||
return (int)winningPlayer;
|
||||
}
|
||||
|
||||
int OutputgameEnd(const CTTRTSGame &game) {
|
||||
std::cout<<"TTRTS: Game Over!"<< std::endl;
|
||||
|
||||
// Get the winning player
|
||||
player_t winningPlayer = game.GetWinningPlayer();
|
||||
|
||||
// Print the winner!
|
||||
if ( winningPlayer != player_t::NUM_INVALID )
|
||||
{
|
||||
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout<<"TTRTS: It was a draw!"<<std::endl;
|
||||
}
|
||||
|
||||
return (int)winningPlayer;
|
||||
}
|
29
source/system/filesystem.h
Normal file
29
source/system/filesystem.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef _TTRTS_FILESYSTEM_H_
|
||||
#define _TTRTS_FILESYSTEM_H_
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "game.h"
|
||||
|
||||
#define STRINGIFY(x) _STRINGIFY(x)
|
||||
#define _STRINGIFY(x) #x
|
||||
|
||||
bool FileExists( const std::string& name );
|
||||
|
||||
void WaitForFile( const std::string& name, const std::chrono::milliseconds& time );
|
||||
|
||||
bool OutputGameStateFile(CTTRTSGame &game);
|
||||
|
||||
std::string GetOrdersFromPlayerFile(const CTTRTSGame &game, player_t &player);
|
||||
|
||||
CTTRTSGame GetGameFromFile( const std::string& file );
|
||||
|
||||
std::string getMapsDir();
|
||||
std::string getGamesDir();
|
||||
|
||||
int runFromFilesystem(int argc, char* argv[]);
|
||||
|
||||
int CreateAndCleanGameDir(const std::string& gameName);
|
||||
|
||||
#endif
|
261
source/system/net.cpp
Normal file
261
source/system/net.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
#include "net.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void WaitForOrdersFromClient(const ClientInfo info, CTTRTSGame &game, std::mutex &mut)
|
||||
{
|
||||
char buffer[1028]; // buffer for orders
|
||||
|
||||
std::clog<<"TTRTS: Waiting for player "<<(int)info.player<<" at "<<inet_ntoa(info.cli_addr.sin_addr)<<std::endl;
|
||||
|
||||
std::string orders;
|
||||
|
||||
while ( orders.find("END") == std::string::npos )
|
||||
{
|
||||
memset(buffer,0,sizeof(buffer));
|
||||
|
||||
// Read in the new socket
|
||||
// read will block until the client has called write
|
||||
// up to the full size of the buffer
|
||||
if (read(info.clientsockfd,buffer,sizeof(buffer)-1) < 0)
|
||||
fatal_perror("ERROR reading from client");
|
||||
|
||||
// Append the received orders
|
||||
orders+=buffer;
|
||||
}
|
||||
|
||||
std::clog<<"TTRTS: Recieved orders from "<<inet_ntoa(info.cli_addr.sin_addr)<<std::endl;
|
||||
|
||||
mut.lock();
|
||||
game.IssueOrders(info.player , orders);
|
||||
mut.unlock();
|
||||
}
|
||||
|
||||
void WaitForOrdersFromClients(std::vector<ClientInfo> &myClients, CTTRTSGame &game, std::mutex &gameMutex)
|
||||
{
|
||||
// Spawn threads
|
||||
std::vector<std::thread> clientThreads;
|
||||
for(auto client : myClients)
|
||||
{
|
||||
std::thread clientThread(WaitForOrdersFromClient, client, std::ref(game), ref(gameMutex));
|
||||
clientThreads.push_back(move(clientThread));
|
||||
}
|
||||
|
||||
// Join up all the threads
|
||||
for ( std::thread& thread : clientThreads )
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void SendGamestateToClients(std::vector<ClientInfo> &myClients, const CTTRTSGame &game, std::mutex &gameMutex)
|
||||
{
|
||||
gameMutex.lock();
|
||||
std::string gamestate_string = GetStringFromGame(game);
|
||||
gameMutex.unlock();
|
||||
|
||||
for (auto client : myClients)
|
||||
{
|
||||
// Write to the socket with the buffer
|
||||
if ( write( client.clientsockfd, gamestate_string.c_str(), gamestate_string.length() ) < 0 )
|
||||
fatal_perror("ERROR sending to client");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PerformClientHandshake(int sockfd, unsigned int &player, std::string &gameNameString)
|
||||
{
|
||||
char handshakeBuffer[128];
|
||||
memset(handshakeBuffer,0,sizeof(handshakeBuffer));
|
||||
|
||||
if (read(sockfd, handshakeBuffer,sizeof(handshakeBuffer)-1) < 0)
|
||||
fatal_perror("ERROR recieving handshake from server");
|
||||
|
||||
std::string handshake(handshakeBuffer);
|
||||
|
||||
if ( write( sockfd, handshake.c_str(), handshake.length()+1 ) < 0 )
|
||||
fatal_perror("ERROR sending handshake to server");
|
||||
|
||||
char gameName[64];
|
||||
if ( sscanf(handshake.c_str(),TTRTS_HANDSHAKE_FORMAT,&player,gameName) < 2 )
|
||||
fatal_error("Handshake failed");
|
||||
|
||||
gameNameString = gameName;
|
||||
}
|
||||
|
||||
void PerformServerHandshake(const ClientInfo &client, const std::string &game)
|
||||
{
|
||||
char handshake[64];
|
||||
snprintf(handshake, sizeof(handshake), TTRTS_HANDSHAKE_FORMAT,(unsigned int)client.player,game.c_str());
|
||||
|
||||
// Send handshake
|
||||
if ( write( client.clientsockfd,handshake,sizeof(handshake) ) < 0 )
|
||||
fatal_perror("ERROR sending to client");
|
||||
|
||||
// Receive handshake
|
||||
char buffer[64];
|
||||
if ( read(client.clientsockfd,buffer,sizeof(buffer)-1) < 0 )
|
||||
fatal_perror("ERROR reading from client");
|
||||
|
||||
// Verify handshake
|
||||
if ( std::string(buffer) != std::string(handshake) )
|
||||
fatal_error("Error in client handshake");
|
||||
|
||||
std::clog<<"TTRTS: Success on handshake with player "<<(int)client.player<< std::endl;
|
||||
}
|
||||
|
||||
void TryBindSocket(int sockfd, const sockaddr_in &serv_addr)
|
||||
{
|
||||
std::clog<<"TTRTS: Binding to socket"<< std::endl;
|
||||
int retry = 1;
|
||||
while (1)
|
||||
{
|
||||
if(retry > 10)
|
||||
fatal_error("Binding failed");
|
||||
|
||||
// Attempt to bind our listening socket
|
||||
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0)
|
||||
break;
|
||||
|
||||
std::cerr<<"Warning: Binding failed on try "<<retry<< std::endl;
|
||||
sleep(retry);
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
|
||||
ClientInfo &WaitForClientConnection(int sockfd, const std::string &game, ClientInfo &clientInfo)
|
||||
{
|
||||
socklen_t clilen = sizeof(sockaddr_in);
|
||||
|
||||
// accept waits for a connection from a client
|
||||
// it returns a new socket file descriptor for this connection
|
||||
// client information will be stored in cli_addr
|
||||
clientInfo.clientsockfd = accept(sockfd, (sockaddr *) &clientInfo.cli_addr, &clilen);
|
||||
if (clientInfo.clientsockfd < 0)
|
||||
fatal_perror("ERROR on accept");
|
||||
|
||||
std::clog<<"TTRTS: Client connected from "<<inet_ntoa(clientInfo.cli_addr.sin_addr)<<" socket "<<clientInfo.clientsockfd<< std::endl;
|
||||
|
||||
// Handshake with client
|
||||
PerformServerHandshake(clientInfo, game);
|
||||
return clientInfo;
|
||||
}
|
||||
|
||||
|
||||
int SetUpServerListeningSocket(const sockaddr_in &serv_addr)
|
||||
{
|
||||
int sockfd;
|
||||
|
||||
std::clog<<"TTRTS: Opening socket"<< std::endl;
|
||||
// Create a new socket
|
||||
// AF_INET is general internet socket domain
|
||||
// SOCK_STREAM as messages will be read in on this socket, SOCK_DGRAM would be for packets
|
||||
// 0 is for default protocol
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
fatal_perror("ERROR opening socket");
|
||||
|
||||
// bind our socket to this server address
|
||||
TryBindSocket(sockfd, serv_addr);
|
||||
|
||||
// Listen on the socket for messages
|
||||
// Second param is length of backlog queue, the maximum number of connections
|
||||
// that can be waiting while the process is handling a single connection
|
||||
// max is usually set to 5
|
||||
listen(sockfd,5);
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
|
||||
sockaddr_in GetLocalServerAddress()
|
||||
{
|
||||
sockaddr_in serv_addr; // Server address
|
||||
|
||||
// empty the server address
|
||||
memset(&serv_addr,0, sizeof(serv_addr));
|
||||
// Set the server address family to AF_INET
|
||||
serv_addr.sin_family = AF_INET;
|
||||
// htons swaps from host byte order to network byte order
|
||||
serv_addr.sin_port = htons(TTRTS_PORT);
|
||||
// The host for this address is this current machine's IP, INADDR_ANY fetches this
|
||||
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
return serv_addr;
|
||||
}
|
||||
|
||||
|
||||
int ConnectToHostServer(const std::string &hostname, sockaddr_in &serv_addr)
|
||||
{
|
||||
// Create a new socket
|
||||
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
fatal_perror("ERROR opening socket");
|
||||
|
||||
// Get the hostent information for the host by name
|
||||
hostent *server = gethostbyname(hostname.c_str());
|
||||
if (server == NULL)
|
||||
fatal_error("ERROR, no such host");
|
||||
|
||||
// copy the server address into our server_addr struct
|
||||
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
|
||||
|
||||
// Attempt to connect to the server using the socket and server address info
|
||||
if (connect(sockfd, (const sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
|
||||
fatal_perror("ERROR connecting");
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
std::string WaitForGamestateMessage(int sockfd)
|
||||
{
|
||||
std::string gamestate;
|
||||
char gamestateBuffer[1028];
|
||||
while( gamestate.find("END") == std::string::npos )
|
||||
{
|
||||
memset(gamestateBuffer,0,sizeof(gamestateBuffer));
|
||||
|
||||
// Receive gamestate
|
||||
if (read(sockfd,gamestateBuffer,sizeof(gamestateBuffer)-1) < 0)
|
||||
fatal_perror("ERROR reading from client");
|
||||
|
||||
gamestate+=gamestateBuffer;
|
||||
}
|
||||
return gamestate;
|
||||
}
|
||||
|
||||
int SendOrdersToServer(int sockfd, const std::string &orders)
|
||||
{
|
||||
int n = write(sockfd,orders.c_str(),orders.length());
|
||||
if (n < 0)
|
||||
fatal_perror("ERROR writing to socket");
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int OutputGameEnd( CTTRTSGame& game )
|
||||
{
|
||||
std::cout<<"TTRTS: Game Over!"<<std::endl;
|
||||
|
||||
// Get the winning player
|
||||
player_t winningPlayer = game.GetWinningPlayer();
|
||||
|
||||
// Print the winner!
|
||||
if ( winningPlayer != player_t::NUM_INVALID )
|
||||
{
|
||||
std::cout<<"TTRTS: Winner:"<<(int) winningPlayer <<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout<<"TTRTS: It was a draw!"<<std::endl;
|
||||
}
|
||||
|
||||
return (int)winningPlayer;
|
||||
}
|
81
source/system/net.h
Normal file
81
source/system/net.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
#ifndef _TTRTS_NET_H_
|
||||
#define _TTRTS_NET_H_
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <game.h>
|
||||
#include <formatters.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#define TTRTS_HANDSHAKE_FORMAT "player %u name %s"
|
||||
|
||||
//======================================================================================================================
|
||||
// Structs for net management
|
||||
|
||||
// Struct for net client info
|
||||
struct ClientInfo
|
||||
{
|
||||
sockaddr_in cli_addr;
|
||||
int clientsockfd;
|
||||
player_t player;
|
||||
};
|
||||
|
||||
|
||||
//======================================================================================================================
|
||||
// Server side function
|
||||
|
||||
// Get the address of a local server
|
||||
sockaddr_in GetLocalServerAddress();
|
||||
|
||||
// Set up a new listening socket for the server
|
||||
int SetUpServerListeningSocket(const sockaddr_in &serv_addr);
|
||||
|
||||
// Wait for client connection on listening socket sockfd
|
||||
// Will fill clientInfo with client information
|
||||
ClientInfo &WaitForClientConnection(int sockfd, const std::string &game, ClientInfo &clientInfo);
|
||||
|
||||
// Wait for orders from a client, will not return until client has send valid orders
|
||||
// Will automatically add orders to the game
|
||||
void WaitForOrdersFromClient(const ClientInfo info, CTTRTSGame &game, std::mutex &mut);
|
||||
|
||||
// Iterates through a list of clients calling WaitForOrdersFromClient
|
||||
void WaitForOrdersFromClients(std::vector<ClientInfo> &myClients, CTTRTSGame &game, std::mutex &gameMutex);
|
||||
|
||||
// Sends current gamestate to each client
|
||||
void SendGamestateToClients(std::vector<ClientInfo> &myClients, const CTTRTSGame &game, std::mutex &gameMutex);
|
||||
|
||||
// Tries to bind to a socket, will attempt 10 times with longer waits between
|
||||
void TryBindSocket(int sockfd, const sockaddr_in &serv_addr);
|
||||
|
||||
// Perform the server side handshake with a client
|
||||
void PerformServerHandshake(const ClientInfo &client, const std::string &game);
|
||||
|
||||
//======================================================================================================================
|
||||
// Client side functions
|
||||
|
||||
// Connect to the host, returns new socket for communication
|
||||
int ConnectToHostServer(const std::string &hostname, sockaddr_in &serv_addr);
|
||||
|
||||
// Perform the client side handshake with the server
|
||||
void PerformClientHandshake(int sockfd, unsigned int &player, std::string &gameNameString);
|
||||
|
||||
// Wait for gamestate message from host
|
||||
std::string WaitForGamestateMessage(int sockfd);
|
||||
|
||||
// Send orders to the server
|
||||
int SendOrdersToServer(int sockfd, const std::string &orders);
|
||||
|
||||
//======================================================================================================================
|
||||
// Other functions
|
||||
int OutputGameEnd( CTTRTSGame& game );
|
||||
|
||||
#endif
|
|
@ -4,8 +4,7 @@
|
|||
project( ttrts-test )
|
||||
|
||||
include_directories(
|
||||
../ttrts
|
||||
../maths
|
||||
../ttrts
|
||||
)
|
||||
|
||||
set( SOURCES
|
||||
|
|
|
@ -5,8 +5,7 @@ project( ttrts )
|
|||
|
||||
# Include the maths
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
../maths
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
# Add our sources
|
||||
|
|
|
@ -92,7 +92,7 @@ CTTRTSGame GetGameFromString( const std::string& input )
|
|||
size_t pos;
|
||||
while ( ( pos = walls_str.find(']') ) != std::string::npos )
|
||||
{
|
||||
std::string pos_string = walls_str.substr(0,pos);
|
||||
std::string pos_string = walls_str.substr(0,pos+1);
|
||||
|
||||
// Use scanf to extract positions
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ CTTRTSGame::CTTRTSGame(CTTRTSGame&& game)
|
|||
, dimensions(std::move(game.dimensions))
|
||||
, turn(std::move(game.turn))
|
||||
, name(std::move(game.name))
|
||||
, m_walls(std::move(game.m_walls))
|
||||
{
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue