Minicraft3DS/source/Synchronizer.c
2018-02-05 19:06:48 +01:00

250 lines
6.4 KiB
C

#include "Synchronizer.h"
#include "Globals.h"
#include "Player.h"
#include "Input.h"
#include "Network.h"
#include "PacketHandler.h"
#include "Ingame.h"
u32 synchronizerLocalTurn;
unsigned int synchronizerNextSeed;
bool synchronizerRunning;
int synchronizerCurrentTicks;
bool synchronizerTurnReady();
void synchronizerNextTurn();
void synchronizerSendLocalInputs();
int synchronizerGetTurnIndex(u32 turn);
void synchronizerInit(int seed, int initPlayerCount, int initPlayerLocalID) {
synchronizerLocalTurn = 0;
synchronizerNextSeed = seed;
playerCount = initPlayerCount;
playerLocalID = initPlayerLocalID;
syncTickCount = 0;
//reset player turn states (e.g. is turn data recieved), first turn needs to happen with no inputs
for(int i=0; i<playerCount; i++) {
resetKeys(&(players[i].inputs));
for(int j=0; j<MAX_INPUT_BUFFER; j++) {
resetKeys(&(players[i].nextInputs[j]));
}
players[i].nextTurnReady[0] = true;
players[i].idSet = false;
players[i].ready = false;
}
players[playerLocalID].id = localUID;
players[playerLocalID].idSet = true;
players[playerLocalID].ready = true;
//switch menu
currentMenu = MENU_LOADING;
synchronizerRunning = false;
}
void synchronizerSendUID() {
sendIDPacket(playerLocalID, localUID);
}
void synchronizerSetPlayerUID(int playerID, u32 uid) {
players[playerID].id = uid;
players[playerID].idSet = true;
}
void synchronizerSendIfReady() {
//we are ready when we recieved all uids
for(int i=0; i<playerCount; i++) {
if(!players[i].idSet) {
return;
}
}
//ready -> send to server
sendStartReadyPacket(playerLocalID);
}
void synchronizerSetPlayerReady(int playerID) {
players[playerID].ready = true;
}
bool synchronizerAllReady() {
for(int i=0; i<playerCount; i++) {
if(!players[i].ready) {
return false;
}
}
return true;
}
void synchronizerStart() {
//check if save file is present
bool doLoad = false;
char *loadName = NULL;
//host and single player need to load the actual file
if(playerLocalID==0) {
FILE *file = fopen(currentFileName, "rb");
if(file!=NULL) {
fclose(file);
doLoad = true;
loadName = currentFileName;
}
//all others the transfered one
} else {
FILE *file = fopen("tmpTransfer.bin", "rb");
if(file!=NULL) {
fclose(file);
doLoad = true;
loadName = "tmpTransfer.bin";
}
}
// reset random generators
srand(synchronizerNextSeed);
gaussrand(true);
//start the game
startGame(doLoad, loadName);
//remove transfered save file from clients
if(playerLocalID!=0) {
FILE *file = fopen("tmpTransfer.bin", "rb");
if(file!=NULL) {
fclose(file);
remove("tmpTransfer.bin");
}
}
//clear menu
currentMenu = MENU_NONE;
synchronizerRunning = true;
synchronizerCurrentTicks = SYNCHRONIZER_TICKS_PER_TURN;
}
void synchronizerTick(void (*gtick)(void)) {
if(synchronizerRunning && synchronizerTurnReady()) {
synchronizerNextTurn();
// reset random generators
srand(synchronizerNextSeed);
gaussrand(true);
syncTickCount++;
//call game tick
(*gtick)();
synchronizerNextSeed = rand();
}
}
//Test if all players (including single player) input is recieved
bool synchronizerTurnReady() {
if(synchronizerCurrentTicks<SYNCHRONIZER_TICKS_PER_TURN) return true;
for(int i=0; i<playerCount; i++) {
if(!players[i].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)]) {
return false;
}
}
return true;
}
void synchronizerNextTurn() {
if(synchronizerCurrentTicks<SYNCHRONIZER_TICKS_PER_TURN) {
synchronizerCurrentTicks++;
//clicked events are only fired for the first tick per turn
for(int i=0; i<playerCount; i++) {
resetClicked(&(players[i].inputs));
}
} else {
//move nextInput of every player to currentInput
for(int i=0; i<playerCount; i++) {
players[i].inputs = players[i].nextInputs[synchronizerGetTurnIndex(synchronizerLocalTurn)];
players[i].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)] = false;
}
//Increase turn number
synchronizerLocalTurn++;
synchronizerCurrentTicks = 1;
//send local input
synchronizerSendLocalInputs();
}
}
void synchronizerSendLocalInputs() {
//scan local inputs
hidScanInput();
tickKeys(&localInputs, hidKeysHeld(), hidKeysDown());
//store local input in nextInput
players[playerLocalID].nextInputs[synchronizerGetTurnIndex(synchronizerLocalTurn)] = localInputs;
players[playerLocalID].nextTurnReady[synchronizerGetTurnIndex(synchronizerLocalTurn)] = true;
//send local input
if(playerCount>1) {
size_t size = writeInputPacket(networkWriteBuffer, &localInputs, playerLocalID, synchronizerLocalTurn);
networkSend(networkWriteBuffer, size);
}
}
void synchronizerOnInputPacket(u8 playerID, u32 turnNumber, void *data, size_t dataSize) {
if(turnNumber>=synchronizerLocalTurn && turnNumber<synchronizerLocalTurn+MAX_INPUT_BUFFER && playerID<playerCount) {
if(readInputPacketData(data, dataSize, &(players[playerID].nextInputs[synchronizerGetTurnIndex(turnNumber)]))) {
players[playerID].nextTurnReady[synchronizerGetTurnIndex(turnNumber)] = true;
}
}
}
int synchronizerGetTurnIndex(u32 turn) {
return turn%MAX_INPUT_BUFFER;
}
void synchronizerReset() {
synchronizerRunning = false;
synchronizerCurrentTicks = 0;
}
bool synchronizerIsRunning() {
return synchronizerRunning;
}
// helpers for random numbers
#define PI 3.141592654
double gaussrand(bool reset)
{
static double U, V;
static int phase = 0;
double Z;
if(reset) {
U = 0;
V = 0;
phase = 0;
return 0;
}
if(phase == 0) {
U = (rand() + 1.) / (RAND_MAX + 2.);
V = rand() / (RAND_MAX + 1.);
Z = sqrt(-2 * log(U)) * sin(2 * PI * V);
} else {
Z = sqrt(-2 * log(U)) * cos(2 * PI * V);
}
phase = 1 - phase;
return Z;
}