mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r207) -Codechange: randomizer handling
-Fix: desync problem fixes -Fix: server doesnt hang anymore when a client timed out -Feature: low latency connection enhancements [*net_sync_freq, *net_ready_ahead]
This commit is contained in:
parent
d03afadad2
commit
1fb915df69
|
@ -1305,6 +1305,8 @@ void PlayersMonthlyLoop()
|
|||
if (_patches.inflation)
|
||||
AddInflation();
|
||||
PlayersPayInterest();
|
||||
// Reset the _current_player flag
|
||||
_current_player = OWNER_NONE;
|
||||
HandleEconomyFluctuations();
|
||||
SubsidyMonthlyHandler();
|
||||
}
|
||||
|
|
|
@ -95,6 +95,8 @@ void memswap(void *a, void *b, size_t size);
|
|||
uint32 Random();
|
||||
uint RandomRange(uint max);
|
||||
|
||||
void InitPlayerRandoms();
|
||||
|
||||
uint32 InteractiveRandom(); /* Used for random sequences that are not the same on the other end of the multiplayer link */
|
||||
void SetDate(uint date);
|
||||
/* facedraw.c */
|
||||
|
@ -131,6 +133,8 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman
|
|||
void NetworkStartSync(bool fcreset);
|
||||
void NetworkClose(bool client);
|
||||
void NetworkSendReadyPacket();
|
||||
void NetworkSendSyncPackets();
|
||||
bool NetworkCheckClientReady();
|
||||
|
||||
void NetworkIPListInit();
|
||||
|
||||
|
|
|
@ -125,8 +125,8 @@ int32 CmdGenRandomNewGame(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|||
// this forces stuff into test mode.
|
||||
_docommand_recursive = 0;
|
||||
|
||||
_random_seed_1 = p1;
|
||||
_random_seed_2 = p2;
|
||||
_random_seeds[0][0] = p1;
|
||||
_random_seeds[0][1] = p2;
|
||||
|
||||
if (_networking) { NetworkStartSync(true); }
|
||||
|
||||
|
@ -166,8 +166,8 @@ int32 CmdStartScenario(int x, int y, uint32 flags, uint32 p1, uint32 p2)
|
|||
// this forces stuff into test mode.
|
||||
_docommand_recursive = 0;
|
||||
|
||||
_random_seed_1 = p1;
|
||||
_random_seed_2 = p2;
|
||||
_random_seeds[0][0] = p1;
|
||||
_random_seeds[0][1] = p2;
|
||||
|
||||
if (_networking) { NetworkStartSync(true); }
|
||||
|
||||
|
|
|
@ -922,8 +922,8 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w)
|
|||
|
||||
void ResetLandscape()
|
||||
{
|
||||
_random_seed_1 = InteractiveRandom();
|
||||
_random_seed_2 = InteractiveRandom();
|
||||
_random_seeds[0][0] = InteractiveRandom();
|
||||
_random_seeds[0][1] = InteractiveRandom();
|
||||
|
||||
GenerateWorld(1);
|
||||
MarkWholeScreenDirty();
|
||||
|
|
40
misc.c
40
misc.c
|
@ -8,8 +8,6 @@
|
|||
extern void StartupEconomy();
|
||||
extern void InitNewsItemStructs();
|
||||
|
||||
static uint32 _random_seed_3, _random_seed_4;
|
||||
|
||||
byte _name_array[512][32];
|
||||
|
||||
static INLINE uint32 ROR(uint32 x, int n)
|
||||
|
@ -20,10 +18,17 @@ static INLINE uint32 ROR(uint32 x, int n)
|
|||
|
||||
uint32 Random()
|
||||
{
|
||||
uint32 t = _random_seed_2;
|
||||
uint32 s = _random_seed_1;
|
||||
_random_seed_1 = s + ROR(t ^ 0x1234567F, 7);
|
||||
return _random_seed_2 = ROR(s, 3);
|
||||
if (_current_player>=MAX_PLAYERS) {
|
||||
uint32 s = _random_seeds[0][0];
|
||||
uint32 t = _random_seeds[0][1];
|
||||
_random_seeds[0][0] = s + ROR(t ^ 0x1234567F, 7);
|
||||
return _random_seeds[0][1] = ROR(s, 3);
|
||||
} else {
|
||||
uint32 s = _player_seeds[_current_player][0];
|
||||
uint32 t = _player_seeds[_current_player][1];
|
||||
_player_seeds[_current_player][0] = s + ROR(t ^ 0x1234567F, 7);
|
||||
return _player_seeds[_current_player][1] = ROR(s, 3);
|
||||
}
|
||||
}
|
||||
|
||||
uint RandomRange(uint max)
|
||||
|
@ -33,10 +38,19 @@ uint RandomRange(uint max)
|
|||
|
||||
uint32 InteractiveRandom()
|
||||
{
|
||||
uint32 t = _random_seed_4;
|
||||
uint32 s = _random_seed_3;
|
||||
_random_seed_3 = s + ROR(t ^ 0x1234567F, 7);
|
||||
return _random_seed_4 = ROR(s, 3);
|
||||
uint32 t = _random_seeds[1][1];
|
||||
uint32 s = _random_seeds[1][0];
|
||||
_random_seeds[1][0] = s + ROR(t ^ 0x1234567F, 7);
|
||||
return _random_seeds[1][1] = ROR(s, 3);
|
||||
}
|
||||
|
||||
void InitPlayerRandoms()
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<MAX_PLAYERS; i++) {
|
||||
_player_seeds[i][0]=InteractiveRandom();
|
||||
_player_seeds[i][1]=InteractiveRandom();
|
||||
}
|
||||
}
|
||||
|
||||
void memswap(void *a, void *b, size_t size) {
|
||||
|
@ -561,7 +575,7 @@ void IncreaseDate()
|
|||
return;
|
||||
_cur_month = ymd.month;
|
||||
|
||||
// printf("Month %d, %X\n", ymd.month, _random_seed_1);
|
||||
// printf("Month %d, %X\n", ymd.month, _random_seeds[0][0]);
|
||||
|
||||
/* yes, call various monthly loops */
|
||||
if (_game_mode != GM_MENU) {
|
||||
|
@ -693,8 +707,8 @@ static const SaveLoadGlobVarList _date_desc[] = {
|
|||
{&_cur_tileloop_tile, SLE_UINT16, 0, 255},
|
||||
{&_disaster_delay, SLE_UINT16, 0, 255},
|
||||
{&_station_tick_ctr, SLE_UINT16, 0, 255},
|
||||
{&_random_seed_1, SLE_UINT32, 0, 255},
|
||||
{&_random_seed_2, SLE_UINT32, 0, 255},
|
||||
{&_random_seeds[0][0], SLE_UINT32, 0, 255},
|
||||
{&_random_seeds[0][1], SLE_UINT32, 0, 255},
|
||||
{&_cur_town_ctr, SLE_UINT8, 0, 255},
|
||||
{&_cur_player_tick_index, SLE_FILE_U8 | SLE_VAR_UINT, 0, 255},
|
||||
{&_next_competitor_start, SLE_FILE_U16 | SLE_VAR_UINT, 0, 255},
|
||||
|
|
279
network.c
279
network.c
|
@ -79,6 +79,15 @@ struct timerequest *TimerRequest = NULL;
|
|||
|
||||
#if defined(ENABLE_NETWORK)
|
||||
|
||||
enum {
|
||||
PACKET_TYPE_WELCOME = 0,
|
||||
PACKET_TYPE_READY,
|
||||
PACKET_TYPE_ACK,
|
||||
PACKET_TYPE_SYNC,
|
||||
PACKET_TYPE_XMIT,
|
||||
PACKET_TYPE_COMMAND,
|
||||
};
|
||||
|
||||
// sent from client -> server whenever the client wants to exec a command.
|
||||
// send from server -> client when another player execs a command.
|
||||
typedef struct CommandPacket {
|
||||
|
@ -92,8 +101,6 @@ typedef struct CommandPacket {
|
|||
uint32 dp[8];
|
||||
} CommandPacket;
|
||||
|
||||
assert_compile(sizeof(CommandPacket) == 16 + 32);
|
||||
|
||||
#define COMMAND_PACKET_BASE_SIZE (sizeof(CommandPacket) - 8 * sizeof(uint32))
|
||||
|
||||
// sent from server -> client periodically to tell the client about the current tick in the server
|
||||
|
@ -107,13 +114,12 @@ typedef struct SyncPacket {
|
|||
uint32 random_seed_2;
|
||||
} SyncPacket;
|
||||
|
||||
assert_compile(sizeof(SyncPacket) == 12);
|
||||
|
||||
// sent from server -> client as an acknowledgement that the server received the command.
|
||||
// the command will be executed at the current value of "max".
|
||||
typedef struct AckPacket {
|
||||
byte packet_length;
|
||||
byte packet_type;
|
||||
byte when;
|
||||
} AckPacket;
|
||||
|
||||
typedef struct ReadyPacket {
|
||||
|
@ -124,16 +130,16 @@ typedef struct ReadyPacket {
|
|||
typedef struct FilePacketHdr {
|
||||
byte packet_length;
|
||||
byte packet_type;
|
||||
byte unused[2];
|
||||
} FilePacketHdr;
|
||||
|
||||
assert_compile(sizeof(FilePacketHdr) == 4);
|
||||
|
||||
// sent from server to client when the client has joined.
|
||||
typedef struct WelcomePacket {
|
||||
byte packet_length;
|
||||
byte packet_type;
|
||||
byte unused[2];
|
||||
uint32 player_seeds[MAX_PLAYERS][2];
|
||||
uint32 frames_max;
|
||||
uint32 frames_srv;
|
||||
uint32 frames_cnt;
|
||||
} WelcomePacket;
|
||||
|
||||
typedef struct Packet Packet;
|
||||
|
@ -155,19 +161,17 @@ typedef struct ClientState {
|
|||
Packet *head, **last;
|
||||
|
||||
uint buflen; // receive buffer len
|
||||
byte buf[256]; // receive buffer
|
||||
byte buf[1024]; // receive buffer
|
||||
} ClientState;
|
||||
|
||||
|
||||
static uint _not_packet;
|
||||
|
||||
typedef struct QueuedCommand QueuedCommand;
|
||||
struct QueuedCommand {
|
||||
QueuedCommand *next;
|
||||
CommandPacket cp;
|
||||
CommandCallback *callback;
|
||||
uint32 cmd;
|
||||
int32 frame;
|
||||
uint32 frame;
|
||||
};
|
||||
|
||||
typedef struct CommandQueue CommandQueue;
|
||||
|
@ -195,7 +199,7 @@ static uint16 _network_ready_ahead = 1;
|
|||
static uint16 _network_client_timeout;
|
||||
|
||||
typedef struct FutureSeeds {
|
||||
int32 frame;
|
||||
uint32 frame;
|
||||
uint32 seed[2];
|
||||
} FutureSeeds;
|
||||
|
||||
|
@ -223,6 +227,8 @@ enum {
|
|||
};
|
||||
|
||||
void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet);
|
||||
static void CloseClient(ClientState *cs);
|
||||
void NetworkSendWelcome(ClientState *cs, bool direct);
|
||||
|
||||
uint32 _network_ip_list[10]; // network ip list
|
||||
|
||||
|
@ -340,6 +346,16 @@ static QueuedCommand *AllocQueuedCommand(CommandQueue *nq)
|
|||
return qp;
|
||||
}
|
||||
|
||||
static void QueueClear(CommandQueue *nq) {
|
||||
QueuedCommand *qp;
|
||||
while ((qp=nq->head)) {
|
||||
// unlink it.
|
||||
if (!(nq->head = qp->next)) nq->last = &nq->head;
|
||||
free(qp);
|
||||
}
|
||||
nq->last = &nq->head;
|
||||
}
|
||||
|
||||
// go through the player queues for each player and see if there are any pending commands
|
||||
// that should be executed this frame. if there are, execute them.
|
||||
void NetworkProcessCommands()
|
||||
|
@ -416,6 +432,20 @@ static void SendBytes(ClientState *cs, void *bytes, uint len)
|
|||
} while (len -= n);
|
||||
}
|
||||
|
||||
// send data direct to a client
|
||||
static void SendDirectBytes(ClientState *cs, void *bytes, uint len)
|
||||
{
|
||||
char *buf = (char*)bytes;
|
||||
uint n;
|
||||
|
||||
n = send(cs->socket, buf, len, 0);
|
||||
if (n == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
DEBUG(net, 0) ("[NET] send() failed with error %d", err);
|
||||
CloseClient(cs);
|
||||
}
|
||||
}
|
||||
|
||||
// client:
|
||||
// add it to the client's ack queue, and send the command to the server
|
||||
// server:
|
||||
|
@ -427,7 +457,7 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman
|
|||
ClientState *cs;
|
||||
|
||||
qp = AllocQueuedCommand(_networking_server ? &_command_queue : &_ack_queue);
|
||||
qp->cp.packet_type = 0;
|
||||
qp->cp.packet_type = PACKET_TYPE_COMMAND;
|
||||
qp->cp.tile = tile;
|
||||
qp->cp.p1 = p1;
|
||||
qp->cp.p2 = p2;
|
||||
|
@ -438,7 +468,7 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman
|
|||
qp->callback = callback;
|
||||
|
||||
// so the server knows when to execute it.
|
||||
qp->frame = _frame_counter_max;
|
||||
qp->frame = _frame_counter + 5;
|
||||
|
||||
// calculate the amount of extra bytes.
|
||||
nump = 8;
|
||||
|
@ -480,6 +510,7 @@ static void HandleCommandPacket(ClientState *cs, CommandPacket *np)
|
|||
QueuedCommand *qp;
|
||||
ClientState *c;
|
||||
AckPacket ap;
|
||||
int i;
|
||||
|
||||
DEBUG(net, 2) ("[NET] cmd size %d", np->packet_length);
|
||||
|
||||
|
@ -489,21 +520,28 @@ static void HandleCommandPacket(ClientState *cs, CommandPacket *np)
|
|||
qp = AllocQueuedCommand(&_command_queue);
|
||||
qp->cp = *np;
|
||||
|
||||
qp->frame = _frame_counter_max;
|
||||
i = _frame_counter_max - (_frame_counter + 3);
|
||||
|
||||
if (i<0) {
|
||||
qp->frame = _frame_counter_max ;
|
||||
} else {
|
||||
qp->frame = _frame_counter + 3;
|
||||
}
|
||||
|
||||
qp->callback = NULL;
|
||||
|
||||
// extra params
|
||||
memcpy(&qp->cp.dp, np->dp, np->packet_length - COMMAND_PACKET_BASE_SIZE);
|
||||
|
||||
ap.packet_type = 2;
|
||||
ap.packet_length = 2;
|
||||
ap.packet_type = PACKET_TYPE_ACK;
|
||||
ap.when = _frame_counter_max-(qp->frame);
|
||||
ap.packet_length = sizeof(AckPacket);
|
||||
|
||||
// send it to the peers
|
||||
if (_networking_server) {
|
||||
for(c=_clients; c->socket != INVALID_SOCKET; c++) {
|
||||
if (c == cs) {
|
||||
SendBytes(c, &ap, 2);
|
||||
SendDirectBytes(c, &ap, ap.packet_length);
|
||||
} else {
|
||||
if (!cs->inactive) SendBytes(c, &qp->cp, qp->cp.packet_length);
|
||||
}
|
||||
|
@ -540,7 +578,7 @@ static void HandleSyncPacket(SyncPacket *sp)
|
|||
s1 = TO_LE32(sp->random_seed_1);
|
||||
s2 = TO_LE32(sp->random_seed_2);
|
||||
|
||||
DEBUG(net, 3) ("[NET] sync seeds: [1]=%i rnd[2]=%i", sp->random_seed_1, sp->random_seed_2);
|
||||
DEBUG(net, 3) ("[NET] sync seeds: frame=%i 1=%i 2=%i",_frame_counter, sp->random_seed_1, sp->random_seed_2);
|
||||
|
||||
if (_frame_counter_srv <= _frame_counter) {
|
||||
// we are ahead of the server check if the seed is in our list.
|
||||
|
@ -561,7 +599,7 @@ static void HandleSyncPacket(SyncPacket *sp)
|
|||
|
||||
// sent from server -> client as an acknowledgement that the server received the command.
|
||||
// the command will be executed at the current value of "max".
|
||||
static void HandleAckPacket()
|
||||
static void HandleAckPacket(AckPacket * ap)
|
||||
{
|
||||
QueuedCommand *q;
|
||||
// move a packet from the ack queue to the end of this player's queue.
|
||||
|
@ -569,12 +607,12 @@ static void HandleAckPacket()
|
|||
assert(q);
|
||||
if (!(_ack_queue.head = q->next)) _ack_queue.last = &_ack_queue.head;
|
||||
q->next = NULL;
|
||||
q->frame = _frame_counter_max;
|
||||
q->frame = (_frame_counter_max - (ap->when));
|
||||
|
||||
*_command_queue.last = q;
|
||||
_command_queue.last = &q->next;
|
||||
|
||||
DEBUG(net, 2) ("[NET] ack");
|
||||
DEBUG(net, 2) ("[NET] ack [frame=%i]",q->frame);
|
||||
}
|
||||
|
||||
static void HandleFilePacket(FilePacketHdr *fp)
|
||||
|
@ -619,6 +657,28 @@ static void HandleFilePacket(FilePacketHdr *fp)
|
|||
}
|
||||
}
|
||||
|
||||
static void HandleWelcomePacket(WelcomePacket *wp) {
|
||||
int i;
|
||||
for (i=0; i<MAX_PLAYERS; i++) {
|
||||
_player_seeds[i][0]=wp->player_seeds[i][0];
|
||||
_player_seeds[i][1]=wp->player_seeds[i][1];
|
||||
}
|
||||
if (wp->frames_srv != 0) {
|
||||
_frame_counter_max = wp->frames_max;
|
||||
_frame_counter_srv = wp->frames_srv;
|
||||
}
|
||||
if (wp->frames_cnt != 0) {
|
||||
_frame_counter = wp->frames_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleReadyPacket(ReadyPacket *rp, ClientState *cs)
|
||||
{
|
||||
cs->ready=true;
|
||||
cs->timeout=_network_client_timeout;
|
||||
}
|
||||
|
||||
|
||||
static void CloseClient(ClientState *cs)
|
||||
{
|
||||
Packet *p, *next;
|
||||
|
@ -680,24 +740,26 @@ static bool ReadPackets(ClientState *cs)
|
|||
size -= packet[0];
|
||||
pos += packet[0];
|
||||
switch(packet[1]) {
|
||||
case 0:
|
||||
case PACKET_TYPE_WELCOME:
|
||||
HandleWelcomePacket((WelcomePacket *)packet);
|
||||
break;
|
||||
case PACKET_TYPE_COMMAND:
|
||||
HandleCommandPacket(cs, (CommandPacket*)packet);
|
||||
break;
|
||||
case 1:
|
||||
case PACKET_TYPE_SYNC:
|
||||
assert(_networking_sync || _networking_queuing);
|
||||
assert(!_networking_server);
|
||||
HandleSyncPacket((SyncPacket*)packet);
|
||||
break;
|
||||
case 2:
|
||||
case PACKET_TYPE_ACK:
|
||||
assert(!_networking_server);
|
||||
HandleAckPacket();
|
||||
HandleAckPacket((AckPacket*)packet);
|
||||
break;
|
||||
case 3:
|
||||
case PACKET_TYPE_XMIT:
|
||||
HandleFilePacket((FilePacketHdr*)packet);
|
||||
break;
|
||||
case 5:
|
||||
cs->ready=true;
|
||||
cs->timeout=_network_client_timeout;
|
||||
case PACKET_TYPE_READY:
|
||||
HandleReadyPacket((ReadyPacket*)packet, cs);
|
||||
break;
|
||||
default:
|
||||
DEBUG (net,0) ("net: unknown packet type");
|
||||
|
@ -783,8 +845,7 @@ static void SendXmit(ClientState *cs)
|
|||
n = minu(_transmit_file_size - pos, 248);
|
||||
|
||||
hdr.packet_length = n + sizeof(hdr);
|
||||
hdr.packet_type = 3;
|
||||
hdr.unused[0] = hdr.unused[1] = 0;
|
||||
hdr.packet_type = PACKET_TYPE_XMIT;
|
||||
SendBytes(cs, &hdr, sizeof(hdr));
|
||||
|
||||
if (n == 0) {
|
||||
|
@ -797,6 +858,10 @@ static void SendXmit(ClientState *cs)
|
|||
|
||||
cs->xmitpos = pos + 1;
|
||||
|
||||
if (cs->xmitpos == 0) {
|
||||
NetworkSendWelcome(cs,false);
|
||||
}
|
||||
|
||||
DEBUG(net, 2) ("[NET] client xmit at %d", pos + 1);
|
||||
}
|
||||
|
||||
|
@ -819,17 +884,65 @@ static ClientState *AllocClient(SOCKET s)
|
|||
|
||||
void NetworkSendReadyPacket()
|
||||
{
|
||||
if (!_network_ready_sent) {
|
||||
if ((!_network_ready_sent) && (_frame_counter + _network_ready_ahead >= _frame_counter_max)) {
|
||||
ReadyPacket *rp = malloc(sizeof(rp));
|
||||
ClientState *c = _clients;
|
||||
|
||||
rp->packet_type = 5;
|
||||
rp->packet_type = PACKET_TYPE_READY;
|
||||
rp->packet_length = sizeof(rp);
|
||||
SendBytes(c, rp, sizeof(rp));
|
||||
_network_ready_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkSendSyncPackets()
|
||||
{
|
||||
ClientState *cs;
|
||||
uint32 new_max;
|
||||
SyncPacket sp;
|
||||
|
||||
new_max = _frame_counter + (int)_network_sync_freq;
|
||||
|
||||
DEBUG(net,3) ("net: serv: sync frame=%i,max=%i, seed1=%i, seed2=%i",new_max,_sync_seed_1,_sync_seed_2);
|
||||
|
||||
sp.packet_length = sizeof(sp);
|
||||
sp.packet_type = PACKET_TYPE_SYNC;
|
||||
sp.frames = new_max - _frame_counter_max;
|
||||
sp.server = _frame_counter_max - _frame_counter;
|
||||
sp.random_seed_1 = TO_LE32(_sync_seed_1);
|
||||
sp.random_seed_2 = TO_LE32(_sync_seed_2);
|
||||
_frame_counter_max = new_max;
|
||||
|
||||
// send it to all the clients and mark them unready
|
||||
for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
|
||||
cs->ready=false;
|
||||
SendBytes(cs, &sp, sizeof(sp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NetworkSendWelcome(ClientState *cs, bool direct) {
|
||||
WelcomePacket wp;
|
||||
int i;
|
||||
wp.packet_type = PACKET_TYPE_WELCOME;
|
||||
wp.packet_length = sizeof(WelcomePacket);
|
||||
for (i=0; i<MAX_PLAYERS; i++) {
|
||||
wp.player_seeds[i][0]=_player_seeds[i][0];
|
||||
wp.player_seeds[i][1]=_player_seeds[i][1];
|
||||
}
|
||||
if (direct) {
|
||||
wp.frames_max=0;
|
||||
wp.frames_srv=0;
|
||||
wp.frames_cnt=_frame_counter;
|
||||
SendDirectBytes(cs,(void *)&wp,wp.packet_length);
|
||||
} else {
|
||||
wp.frames_max=_frame_counter_max;
|
||||
wp.frames_srv=_frame_counter_srv;
|
||||
wp.frames_cnt=0;
|
||||
SendBytes(cs,(void *)&wp,wp.packet_length);
|
||||
}
|
||||
}
|
||||
|
||||
static void NetworkAcceptClients()
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
|
@ -888,12 +1001,12 @@ static void SendQueuedCommandsToNewClient(ClientState *cs)
|
|||
// send the commands in the server queue to the new client.
|
||||
QueuedCommand *qp;
|
||||
SyncPacket sp;
|
||||
int32 frame;
|
||||
uint32 frame;
|
||||
|
||||
DEBUG(net, 2) ("[NET] sending queued commands to client");
|
||||
|
||||
sp.packet_length = sizeof(sp);
|
||||
sp.packet_type = 1;
|
||||
sp.packet_type = PACKET_TYPE_SYNC;
|
||||
sp.random_seed_1 = sp.random_seed_2 = 0;
|
||||
sp.server = 0;
|
||||
|
||||
|
@ -918,6 +1031,25 @@ static void SendQueuedCommandsToNewClient(ClientState *cs)
|
|||
|
||||
}
|
||||
|
||||
|
||||
bool NetworkCheckClientReady() {
|
||||
bool ready_all = true;
|
||||
uint16 count = 0;
|
||||
ClientState *cs;
|
||||
|
||||
for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
|
||||
count++;
|
||||
ready_all = ready_all && (cs->ready || cs->inactive || (cs->xmitpos>0));
|
||||
if (!cs->ready) cs->timeout-=1;
|
||||
if (cs->timeout == 0) {
|
||||
SET_DPARAM16(0,count);
|
||||
ShowErrorMessage(-1,STR_NETWORK_ERR_TIMEOUT,0,0);
|
||||
CloseClient(cs);
|
||||
}
|
||||
}
|
||||
return ready_all;
|
||||
}
|
||||
|
||||
// ************************** //
|
||||
// * TCP Networking * //
|
||||
// ************************** //
|
||||
|
@ -1077,6 +1209,8 @@ void NetworkReceive()
|
|||
|
||||
// send queue of commands to client.
|
||||
SendQueuedCommandsToNewClient(cs);
|
||||
|
||||
NetworkSendWelcome(cs, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1086,61 +1220,6 @@ void NetworkSend()
|
|||
{
|
||||
ClientState *cs;
|
||||
void *free_xmit;
|
||||
uint16 count;
|
||||
bool ready_all;
|
||||
|
||||
// send sync packets?
|
||||
if (_networking_server && _networking_sync && !_pause) {
|
||||
|
||||
if (++_not_packet >= _network_sync_freq) {
|
||||
SyncPacket sp;
|
||||
uint new_max;
|
||||
|
||||
_network_ahead_frames = _network_sync_freq + 1;
|
||||
|
||||
ready_all=false;
|
||||
|
||||
while (!ready_all) {
|
||||
// check wether all clients are ready
|
||||
ready_all=true;
|
||||
count=0;
|
||||
for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
|
||||
count++;
|
||||
ready_all = ready_all && (cs->ready || cs->inactive || (cs->xmitpos>0));
|
||||
if (!cs->ready) cs->timeout-=5;
|
||||
if (cs->timeout == 0) {
|
||||
SET_DPARAM16(0,count);
|
||||
ShowErrorMessage(-1,STR_NETWORK_ERR_TIMEOUT,0,0);
|
||||
CloseClient(cs);
|
||||
}
|
||||
}
|
||||
if (!ready_all) {
|
||||
NetworkReceive();
|
||||
CSleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
_not_packet = 0;
|
||||
|
||||
new_max = max(_frame_counter + (int)_network_ahead_frames, _frame_counter_max);
|
||||
|
||||
DEBUG(net,3) ("net: serv: sync max=%i, seed1=%i, seed2=%i",new_max,_sync_seed_1,_sync_seed_2);
|
||||
|
||||
sp.packet_length = sizeof(sp);
|
||||
sp.packet_type = 1;
|
||||
sp.frames = new_max - _frame_counter_max;
|
||||
sp.server = _frame_counter_max - _frame_counter;
|
||||
sp.random_seed_1 = TO_LE32(_sync_seed_1);
|
||||
sp.random_seed_2 = TO_LE32(_sync_seed_2);
|
||||
_frame_counter_max = new_max;
|
||||
|
||||
// send it to all the clients and mark them unready
|
||||
for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
|
||||
cs->ready=false;
|
||||
SendBytes(cs, &sp, sizeof(sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_xmit = _transmit_file;
|
||||
|
||||
|
@ -1168,8 +1247,9 @@ void NetworkInitialize()
|
|||
{
|
||||
ClientState *cs;
|
||||
|
||||
QueueClear(&_command_queue);
|
||||
QueueClear(&_ack_queue);
|
||||
_command_queue.last = &_command_queue.head;
|
||||
_ack_queue.last = &_ack_queue.head;
|
||||
|
||||
// invalidate all clients
|
||||
for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++)
|
||||
|
@ -1455,7 +1535,7 @@ void NetworkCoreInit() {
|
|||
|
||||
DEBUG(net, 3) ("[NET][Core] init()");
|
||||
_network_available=true;
|
||||
_network_client_timeout=3000;
|
||||
_network_client_timeout=300;
|
||||
|
||||
// [win32] winsock startup
|
||||
|
||||
|
@ -1631,19 +1711,12 @@ if (incomming) {
|
|||
|
||||
if (_networking) {
|
||||
NetworkReceive();
|
||||
NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it.
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// outgoing
|
||||
|
||||
if ((_networking) && (!_networking_server) && (_frame_counter+_network_ready_ahead >= _frame_counter_max)) {
|
||||
// send the "i" am ready message to the server
|
||||
// [_network_ready_ahead] frames before "i" reach the frame-limit
|
||||
NetworkSendReadyPacket();
|
||||
}
|
||||
|
||||
if ( _udp_client_socket != INVALID_SOCKET ) NetworkUDPReceive(true);
|
||||
if ( _udp_server_socket != INVALID_SOCKET ) NetworkUDPReceive(false);
|
||||
|
||||
if (_networking) {
|
||||
NetworkSend();
|
||||
|
|
|
@ -31,10 +31,17 @@ static byte _players_max;
|
|||
* we'll just use some dummy here
|
||||
*/
|
||||
static byte _network_connection;
|
||||
static uint16 _network_game_count_last;
|
||||
|
||||
static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
|
||||
{
|
||||
switch(e->event) {
|
||||
case WE_TICK: {
|
||||
if (_network_game_count_last != _network_game_count) {
|
||||
SetWindowDirty(w);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WE_PAINT: {
|
||||
|
||||
SET_DPARAM16(0, 0x00);
|
||||
|
@ -86,8 +93,18 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
|
|||
|
||||
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
|
||||
_network_connection = e->dropdown.index;
|
||||
|
||||
SetWindowDirty(w);
|
||||
switch (_network_connection) {
|
||||
case 0:
|
||||
NetworkGameListFromLAN();
|
||||
_network_game_count_last = _network_game_count;
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
case 1:
|
||||
NetworkGameListFromInternet();
|
||||
_network_game_count_last = _network_game_count;
|
||||
SetWindowDirty(w);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WE_MOUSELOOP:
|
||||
|
@ -164,6 +181,7 @@ void ShowNetworkGameWindow()
|
|||
w = AllocateWindowDesc(&_network_game_window_desc);
|
||||
strcpy(_edit_str_buf, "Your name");
|
||||
|
||||
_network_game_count_last = _network_game_count;
|
||||
|
||||
WP(w,querystr_d).caret = 1;
|
||||
WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
|
||||
|
|
|
@ -1124,8 +1124,8 @@ bool LoadOldSaveGame(const char *file)
|
|||
_cur_tileloop_tile = m->cur_tileloop_tile;
|
||||
_disaster_delay = m->disaster_delay;
|
||||
_station_tick_ctr = m->station_tick_ctr;
|
||||
_random_seed_1 = m->seed_1;
|
||||
_random_seed_2 = m->seed_2;
|
||||
_random_seeds[0][0] = m->seed_1;
|
||||
_random_seeds[0][1] = m->seed_2;
|
||||
_cur_town_ctr = REMAP_TOWN_IDX(m->cur_town_ptr);
|
||||
_cur_player_tick_index = m->cur_player_tick_index;
|
||||
_next_competitor_start = m->next_competitor_start;
|
||||
|
|
|
@ -548,7 +548,7 @@ void RunOtherPlayersLoop()
|
|||
_is_ai_player = true;
|
||||
|
||||
FOR_ALL_PLAYERS(p) {
|
||||
if (p->is_active) {
|
||||
if (p->is_active && p->is_ai) {
|
||||
_current_player = p->index;
|
||||
if (_patches.ainew_active)
|
||||
AiNewDoGameLoop(p);
|
||||
|
@ -558,8 +558,7 @@ void RunOtherPlayersLoop()
|
|||
}
|
||||
|
||||
_is_ai_player = false;
|
||||
// XXX: is this needed?
|
||||
_current_player = 0;
|
||||
_current_player = OWNER_NONE;
|
||||
}
|
||||
|
||||
// index is the next parameter in _decode_parameters to set up
|
||||
|
|
34
ttd.c
34
ttd.c
|
@ -533,7 +533,7 @@ int ttd_main(int argc, char* argv[])
|
|||
_switch_mode = SM_NEWGAME;
|
||||
break;
|
||||
case 'G':
|
||||
_random_seed_1 = atoi(mgo.opt);
|
||||
_random_seeds[0][0] = atoi(mgo.opt);
|
||||
break;
|
||||
case 'p': {
|
||||
int i = atoi(mgo.opt);
|
||||
|
@ -604,6 +604,7 @@ int ttd_main(int argc, char* argv[])
|
|||
|
||||
// initialize the ingame console
|
||||
IConsoleInit();
|
||||
InitPlayerRandoms();
|
||||
|
||||
while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
|
||||
|
||||
|
@ -881,8 +882,8 @@ void StateGameLoop()
|
|||
_frame_counter++;
|
||||
|
||||
// store the random seed to be able to detect out of sync errors
|
||||
_sync_seed_1 = _random_seed_1;
|
||||
_sync_seed_2 = _random_seed_2;
|
||||
_sync_seed_1 = _random_seeds[0][0];
|
||||
_sync_seed_2 = _random_seeds[0][1];
|
||||
if (_networking) disable_computer=true;
|
||||
|
||||
if (_savedump_path[0] && (uint)_frame_counter >= _savedump_first && (uint)(_frame_counter -_savedump_first) % _savedump_freq == 0 ) {
|
||||
|
@ -899,6 +900,11 @@ void StateGameLoop()
|
|||
CallWindowTickEvent();
|
||||
NewsLoop();
|
||||
} else {
|
||||
// All these actions has to be done from OWNER_NONE
|
||||
// for multiplayer compatibility
|
||||
uint p = _current_player;
|
||||
_current_player = OWNER_NONE;
|
||||
|
||||
AnimateAnimatedTiles();
|
||||
IncreaseDate();
|
||||
RunTileLoop();
|
||||
|
@ -910,6 +916,7 @@ void StateGameLoop()
|
|||
|
||||
CallWindowTickEvent();
|
||||
NewsLoop();
|
||||
_current_player = p;
|
||||
}
|
||||
_in_state_game_loop = false;
|
||||
}
|
||||
|
@ -985,21 +992,36 @@ void GameLoop()
|
|||
NetworkCoreLoop(true);
|
||||
|
||||
if (_networking_sync) {
|
||||
// make sure client's time is synched to the server by running frames quickly up to where the server is.
|
||||
// client: make sure client's time is synched to the server by running frames quickly up to where the server is.
|
||||
if (!_networking_server) {
|
||||
while (_frame_counter < _frame_counter_srv) {
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands(); // need to process queue to make sure that packets get executed.
|
||||
}
|
||||
}
|
||||
// don't exceed the max count told by the server
|
||||
// client: don't exceed the max count told by the server
|
||||
if (_frame_counter < _frame_counter_max) {
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands();
|
||||
}
|
||||
// client: send the ready packet
|
||||
NetworkSendReadyPacket();
|
||||
} else {
|
||||
// server: work on to the frame max
|
||||
if (_frame_counter < _frame_counter_max) {
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it.
|
||||
}
|
||||
// server: wait until all clients were ready for going on
|
||||
if (_frame_counter == _frame_counter_max) {
|
||||
if (NetworkCheckClientReady()) NetworkSendSyncPackets();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// server/client/standalone: not synced --> state game loop
|
||||
if (!_pause)
|
||||
StateGameLoop();
|
||||
// server/client: process queued network commands
|
||||
if (_networking) NetworkProcessCommands();
|
||||
}
|
||||
|
||||
if (!_pause && _display_opt&DO_FULL_ANIMATION)
|
||||
|
|
2
unix.c
2
unix.c
|
@ -420,7 +420,7 @@ int CDECL main(int argc, char* argv[])
|
|||
ChangeWorkingDirectory(argv[0]);
|
||||
#endif
|
||||
|
||||
_random_seed_2 = _random_seed_1 = time(NULL);
|
||||
_random_seeds[0][1] = _random_seeds[0][0] = time(NULL);
|
||||
|
||||
|
||||
return ttd_main(argc, argv);
|
||||
|
|
10
variables.h
10
variables.h
|
@ -62,7 +62,9 @@ VARDEF uint16 _disaster_delay;
|
|||
// tick handler.
|
||||
VARDEF uint16 _station_tick_ctr;
|
||||
|
||||
VARDEF uint32 _random_seed_1, _random_seed_2;
|
||||
VARDEF uint32 _random_seeds[2][2];
|
||||
VARDEF uint32 _player_seeds[MAX_PLAYERS][2];
|
||||
|
||||
// Iterator through all towns in OnTick_Town
|
||||
VARDEF byte _cur_town_ctr;
|
||||
|
||||
|
@ -214,10 +216,10 @@ VARDEF byte _cur_month;
|
|||
VARDEF byte _player_colors[MAX_PLAYERS];
|
||||
|
||||
VARDEF bool _in_state_game_loop;
|
||||
VARDEF int32 _frame_counter;
|
||||
VARDEF uint32 _frame_counter;
|
||||
|
||||
VARDEF int32 _frame_counter_max; // for networking, this is the frame that we are not allowed to execute yet.
|
||||
VARDEF int32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max.
|
||||
VARDEF uint32 _frame_counter_max; // for networking, this is the frame that we are not allowed to execute yet.
|
||||
VARDEF uint32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max.
|
||||
|
||||
// networking settings
|
||||
VARDEF bool _network_available; // is network mode available?
|
||||
|
|
8
win32.c
8
win32.c
|
@ -1951,12 +1951,12 @@ int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLin
|
|||
#if defined(_MSC_VER)
|
||||
{
|
||||
uint64 seed = rdtsc();
|
||||
_random_seed_1 = ((uint32*)&seed)[0];
|
||||
_random_seed_2 = ((uint32*)&seed)[1];
|
||||
_random_seeds[0][0] = ((uint32*)&seed)[0];
|
||||
_random_seeds[0][1] = ((uint32*)&seed)[1];
|
||||
}
|
||||
#else
|
||||
_random_seed_1 = GetTickCount();
|
||||
_random_seed_2 = _random_seed_1 * 0x1234567;
|
||||
_random_seeds[0][0] = GetTickCount();
|
||||
_random_seeds[0][1] = _random_seeds[0][0] * 0x1234567;
|
||||
#endif
|
||||
|
||||
argc = ParseCommandLine(GetCommandLine(), argv, lengthof(argv));
|
||||
|
|
Loading…
Reference in New Issue