2020-09-30 17:17:25 +02:00

953 lines
26 KiB
C

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
xirm_server.c
Abstract:
XInput remote server program
--*/
#include "precomp.h"
#include "xinput_remote.h"
#include "list.h"
// Our module name for debug spews
#define MYMODNAME "XIRM"
BOOL dosetup;
SOCKET sock, listensock;
HANDLE recvevt;
CHAR recvbuf[XIRM_MAX_PACKET_SIZE];
struct sockaddr_in clientaddr;
#define CLIENTADDR clientaddr.sin_addr.s_addr
BOOL autonetmode;
CHAR clientname[XIRM_MACHINE_NAME_MAXLEN];
DWORD connecttime;
DWORD clientisn, clientseq;
DWORD serverseq_una, serverseq_nxt;
INT fastrexmit, rexmit_timer, rexmit_count;
BOOL rexmit_disabled;
LIST_ENTRY sendq;
struct ConnectionRequest {
DWORD clientisn;
struct sockaddr_in clientaddr;
CHAR clientname[XIRM_MACHINE_NAME_MAXLEN];
} connection_request;
CRITICAL_SECTION connection_request_lock;
#define ConnectReqLock() EnterCriticalSection(&connection_request_lock)
#define ConnectReqUnlock() LeaveCriticalSection(&connection_request_lock)
#define HasPendingConnectReq() (connection_request.clientaddr.sin_addr.s_addr != 0)
__inline BOOL GetPendingConnectReq(struct ConnectionRequest* connreq) {
ConnectReqLock();
*connreq = connection_request;
connection_request.clientaddr.sin_addr.s_addr = 0;
ConnectReqUnlock();
return (connreq->clientaddr.sin_addr.s_addr != 0);
}
#define GetSendqHead() ((SendBuf*) sendq.Flink)
#define GetSendqLen() ((INT) (serverseq_nxt - serverseq_una))
#define IsSendqFull() (GetSendqLen() >= XIRM_SEND_WINDOW)
#define IsSendqEmpty() (serverseq_una == serverseq_nxt)
typedef struct _SendBuf {
LIST_ENTRY links;
INT datalen;
XIrmPacketHeader data;
} SendBuf;
DWORD active_device_masks;
DWORD pending_insertions;
DWORD pending_removals;
struct DeviceData {
HANDLE handle;
XINPUT_STATE current_states;
XINPUT_STATE last_states;
XINPUT_CAPABILITIES devicecaps;
} devices[XIRM_PORTMAX];
//
// Main server loop sampling interval (10 msecs = 100Hz)
//
#define SAMPLING_INTERVAL 10
//
// Read the processor timestamp counter in milliseconds
//
__inline DWORD ReadTimestamp() {
__asm {
rdtsc
mov ebx, 733000
div ebx
}
}
//
// Read the client configuration file
//
#define CONFIGFILENAME "d:\\xirmsrv.cfg"
BOOL
ReadConfig()
{
CHAR buf[256];
FILE* fin = fopen(CONFIGFILENAME, "r");
if (!fin) {
XDBGTRC(MYMODNAME, "cannot open config file");
return FALSE;
}
while (fgets(buf, sizeof(buf), fin)) {
CHAR *p, *val;
p = strchr(buf, '=');
if (!p) continue;
*p++ = '\0';
while (*p && isspace(*p)) p++;
val = p;
while (*p && *p != '\r' && *p != '\n' && !isspace(*p)) p++;
*p = '\0';
if (*val == '\0') continue;
if (strcmp(buf, "name") == 0) {
if (strlen(val) < XIRM_MACHINE_NAME_MAXLEN) {
strcpy(clientname, val);
}
} else if (strcmp(buf, "ipaddr") == 0) {
ULONG addr = inet_addr(val);
if (addr != INADDR_NONE)
CLIENTADDR = addr;
}
}
fclose(fin);
XDBGTRC(MYMODNAME, "client config: %s %u.%u.%u.%u", clientname, clientaddr.sin_addr.S_un.S_un_b.s_b1, clientaddr.sin_addr.S_un.S_un_b.s_b2, clientaddr.sin_addr.S_un.S_un_b.s_b3, clientaddr.sin_addr.S_un.S_un_b.s_b4);
return (clientname[0] != '\0' || CLIENTADDR != 0);
}
//
// Write the client configuration file
//
BOOL
WriteConfig()
{
FILE* fout = fopen(CONFIGFILENAME, "w");
if (!fout) return FALSE;
fprintf(fout, "name=%s\n", clientname[0] ? clientname : "<NONE>");
fprintf(fout, "ipaddr=%u.%u.%u.%u\n", clientaddr.sin_addr.S_un.S_un_b.s_b1, clientaddr.sin_addr.S_un.S_un_b.s_b2, clientaddr.sin_addr.S_un.S_un_b.s_b3, clientaddr.sin_addr.S_un.S_un_b.s_b4);
fclose(fout);
return TRUE;
}
//
// Close the specified port
//
VOID ClosePort(DWORD port) {
struct DeviceData* device = &devices[port];
if (device->handle) {
XInputClose(device->handle);
memset(device, 0, sizeof(*device));
}
}
//
// Open the specified port
//
BOOL OpenPort(DWORD port) {
struct DeviceData* device = &devices[port];
DWORD err;
ASSERT(device->handle == NULL);
device->handle = XInputOpen(
XDEVICE_TYPE_GAMEPAD,
port,
XDEVICE_NO_SLOT,
NULL);
if (device->handle == NULL) {
XDBGWRN(MYMODNAME, "XInputOpen failed: %d", GetLastError());
return FALSE;
}
err = XInputGetCapabilities(device->handle, &device->devicecaps);
if (err != ERROR_SUCCESS) {
XDBGWRN(MYMODNAME, "XInputGetCapabilities failed: %d", err);
return FALSE;
}
return TRUE;
}
//
// Poll the input states
//
VOID
PollInput()
{
DWORD insertions, removals;
DWORD port;
// Check if there has been any device changes
if (XGetDeviceChanges(XDEVICE_TYPE_GAMEPAD, &insertions, &removals)) {
// close devices that have been removed
for (port=0; port < XIRM_PORTMAX; port++) {
if (removals & (1 << port)) {
if (devices[port].handle) {
ClosePort(port);
}
}
}
// open devices that have been inserted
for (port=0; port < XIRM_PORTMAX; port++) {
if (insertions & (1 << port)) {
if (!OpenPort(port)) {
// If we failed to open the port, treat it as not plugged in
removals |= (1 << port);
insertions &= ~(1 << port);
ClosePort(port);
}
}
}
active_device_masks &= ~removals;
active_device_masks |= insertions;
pending_removals |= removals;
pending_insertions &= ~removals;
pending_insertions |= insertions;
}
// Now for currently inserted devices, update the state information
for (port=0; port < XIRM_PORTMAX; port++) {
struct DeviceData* device = &devices[port];
if (device->handle) {
DWORD err = XInputGetState(device->handle, &device->current_states);
if (err != ERROR_SUCCESS) {
XDBGWRN(MYMODNAME, "XInputGetState failed: %d", err);
device->current_states = device->last_states;
}
}
}
}
//
// Initialize the input module
//
#define MAGIC_KEYCOMBO (XINPUT_GAMEPAD_START | XINPUT_GAMEPAD_BACK)
VOID
InitInput()
{
DWORD port;
XInitDevices(0, NULL);
Sleep(2000);
// Get the initial device states
PollInput();
// Look for the special key combination to enter Setup mode
for (port=0; port < XIRM_PORTMAX; port++) {
if (devices[port].handle &&
devices[port].current_states.Gamepad.wButtons == MAGIC_KEYCOMBO &&
devices[port].current_states.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X]) {
dosetup = TRUE;
break;
}
}
}
//
// Transmit the first packet in the send queue
//
#define SetRexmitTimer() (rexmit_timer = ReadTimestamp() + XIRM_RETRANSMIT_TIMEOUT)
__inline VOID EnableRexmit() {
if (rexmit_disabled) {
rexmit_disabled = FALSE;
rexmit_count = 1;
}
}
VOID
SendqXmit(
SendBuf* sendbuf
)
{
INT count;
count = send(sock, (CHAR*) &sendbuf->data, sendbuf->datalen, 0);
if (count != sendbuf->datalen) {
XDBGWRN(MYMODNAME, "send failed: %d, %d", count, GetLastError());
}
if (sendbuf == GetSendqHead()) {
SetRexmitTimer();
}
rexmit_count++;
fastrexmit = 0;
}
//
// Do retransmission if needed
//
VOID
CheckRexmit()
{
if (rexmit_disabled || IsSendqEmpty()) return;
if ((INT) (rexmit_timer - ReadTimestamp()) < 0) {
if (rexmit_count >= XIRM_RETRANSMIT_RETRY) {
// If too many retries, we assume the client is down
// and stop retransmitting until we receive a packet
// from the client or there is an input state change.
XDBGTRC(MYMODNAME, "client down?");
rexmit_disabled = TRUE;
} else {
SendqXmit(GetSendqHead());
}
}
}
//
// Insert a new send buffer to the end of the send queue
// and then transmit it out to the client
//
VOID
InsertSendBuf(
SendBuf* sendbuf
)
{
if (IsListEmpty(&sendq)) rexmit_count = 0;
InsertTailList(&sendq, &sendbuf->links);
SendqXmit(sendbuf);
}
//
// Allocate memory buffer for a new outgoing packet
//
SendBuf*
AllocSendBuf(
INT size
)
{
SendBuf* sendbuf = (SendBuf*) malloc(offsetof(SendBuf, data) + size);
if (sendbuf) {
sendbuf->datalen = size;
sendbuf->data.sendseq = serverseq_nxt++;
sendbuf->data.ackseq = clientseq;
} else {
XDBGWRN(MYMODNAME, "out of memory");
}
return sendbuf;
}
//
// Send device changes to the client
//
VOID
SendDeviceChanges()
{
SendBuf* sendbuf;
XIrmPacketDeviceChanges* devchgpkt;
XINPUT_CAPABILITIES* devcaps;
INT pktlen;
DWORD port;
// Return immediately if:
// there has been no changes, or
// the send queue is full
if ((pending_insertions | pending_removals) == 0 || IsSendqFull())
return;
pktlen = offsetof(XIrmPacketDeviceChanges, devicecaps) +
XIrmCountDevices(active_device_masks) * sizeof(XINPUT_CAPABILITIES);
sendbuf = AllocSendBuf(pktlen);
if (!sendbuf) return;
devchgpkt = (XIrmPacketDeviceChanges*) &sendbuf->data;
devchgpkt->header.type = PKTTYPE_DEVICE_CHANGES;
devchgpkt->active_devices = active_device_masks;
devchgpkt->insertions = pending_insertions;
devchgpkt->removals = pending_removals;
// NOTE: We don't bother with keyboard support on silver box.
devchgpkt->keyboard_device = 0;
pending_insertions = pending_removals = 0;
devcaps = devchgpkt->devicecaps;
for (port=0; port < XIRM_PORTMAX; port++) {
if (active_device_masks & (1 << port)) {
*devcaps++ = devices[port].devicecaps;
}
}
// Start retransmission again if input state has changed
EnableRexmit();
InsertSendBuf(sendbuf);
}
//
// Send input state changes to the client
//
VOID
SendStateChanges()
{
DWORD port, masks = 0;
INT pktlen = 0;
SendBuf* sendbuf;
XIrmPacketStateChanges* stchgpkt;
XINPUT_STATE* states;
if (IsSendqFull()) return;
// Check if there has been any state changes
for (port=0; port < XIRM_PORTMAX; port++) {
if (devices[port].handle) {
ASSERT(active_device_masks & (1 << port));
if (memcmp(&devices[port].current_states,
&devices[port].last_states,
sizeof(XINPUT_STATE)) != 0) {
masks |= (1 << port);
pktlen++;
}
}
}
if (masks == 0) return;
pktlen = offsetof(XIrmPacketStateChanges, states) + pktlen * sizeof(XINPUT_STATE);
sendbuf = AllocSendBuf(pktlen);
if (!sendbuf) return;
stchgpkt = (XIrmPacketStateChanges*) &sendbuf->data;
stchgpkt->header.type = PKTTYPE_STATE_CHANGES;
stchgpkt->device_masks = masks;
states = stchgpkt->states;
for (port=0; port < XIRM_PORTMAX; port++) {
if (masks & (1 << port)) {
*states++ = devices[port].last_states = devices[port].current_states;
}
}
// Start retransmission again if input state has changed
EnableRexmit();
InsertSendBuf(sendbuf);
}
//
// Initialize the connection with the client
//
VOID
InitClient(
struct ConnectionRequest* connreq
)
{
SendBuf* sendbuf;
DWORD port;
INT err;
strcpy(clientname, connreq->clientname);
clientaddr = connreq->clientaddr;
XDBGTRC(MYMODNAME,
"connected to client: %s %u.%u.%u.%u:%d",
clientname,
clientaddr.sin_addr.S_un.S_un_b.s_b1, clientaddr.sin_addr.S_un.S_un_b.s_b2, clientaddr.sin_addr.S_un.S_un_b.s_b3, clientaddr.sin_addr.S_un.S_un_b.s_b4,
ntohs(clientaddr.sin_port));
err = connect(sock, (struct sockaddr*) &clientaddr, sizeof(clientaddr));
ASSERT(err == NO_ERROR);
connecttime = ReadTimestamp();
rexmit_disabled = FALSE;
clientisn = connreq->clientisn;
clientseq = clientisn+1;
serverseq_una = serverseq_nxt = XIrmGetISN();
while (!IsListEmpty(&sendq)) {
LIST_ENTRY* p = RemoveHeadList(&sendq);
free(p);
}
sendbuf = AllocSendBuf(sizeof(XIrmPacketHeader));
ASSERT(sendbuf != NULL);
sendbuf->data.type = PKTTYPE_SERVER_INIT;
InsertSendBuf(sendbuf);
// Send the initial device caps and states
pending_insertions = active_device_masks;
pending_removals = 0;
for (port=0; port < XIRM_PORTMAX; port++) {
memset(&devices[port].last_states, 0, sizeof(devices[port].last_states));
}
SendDeviceChanges();
SendStateChanges();
}
//
// Process a set-state packet from the client
//
VOID
ProcessClientSetState(
XIrmPacketSetState* pkt
)
{
static XINPUT_FEEDBACK feedback;
DWORD err;
if (pkt->port < XIRM_PORTMAX &&
devices[pkt->port].handle &&
feedback.Header.dwStatus != ERROR_IO_PENDING) {
feedback = pkt->feedback;
err = XInputSetState(devices[pkt->port].handle, &feedback);
if (err != ERROR_SUCCESS &&
err != ERROR_IO_PENDING &&
err != ERROR_NOT_SUPPORTED) {
XDBGWRN(MYMODNAME, "XInputSetState failed: %d", err);
}
}
}
//
// Check to see if we should serve an incoming connection request from a client
//
__inline BOOL IsAutonetAddr(ULONG addr) {
// autonet address range is 169.254.x.x/16
addr = ntohl(addr);
return (addr & 0xffff0000) == 0xa9fe0000;
}
INT
MatchClient(
CHAR* name,
ULONG addr
)
{
XNADDR XnAddr;
static struct in_addr myaddr;
// Try name match first
if (clientname[0] && strcmp(clientname, name) == 0) return 1;
// Then try explicit address match
if (CLIENTADDR && CLIENTADDR == addr) return 2;
// Now check to see if both client and server are using autonet addresses
if (IsAutonetAddr(addr)) {
if (myaddr.s_addr == 0) {
XNetGetTitleXnAddr(&XnAddr);
CopyMemory(&myaddr, &XnAddr.ina, sizeof(myaddr));
}
if (IsAutonetAddr(myaddr.s_addr)) return 3;
}
return 0;
}
//
// Process a client-init packet from the client
//
VOID
ProcessClientInit()
{
struct ConnectionRequest connreq;
if (GetPendingConnectReq(&connreq) &&
MatchClient(connreq.clientname, connreq.clientaddr.sin_addr.s_addr)) {
// Figure out how long has it been since the last connect
INT time = ReadTimestamp() - connecttime;
if (connreq.clientisn == clientisn && time >= 0 && time <= 3000) {
// The packet is a duplicate - just drop it
} else {
// The client must have rebooted, reestablish connection
XDBGTRC(MYMODNAME, "client must have rebooted");
InitClient(&connreq);
}
}
}
//
// Process an acknowledgement packet from the client
//
VOID
ProcessClientAck(
XIrmPacketHeader* pkt
)
{
DWORD ack;
INT index, sent;
ack = pkt->ackseq;
index = (INT) (ack - serverseq_una);
sent = GetSendqLen();
if (index > 0 && index <= sent) {
// The acknowledgement is valid
ASSERT(!IsListEmpty(&sendq));
serverseq_una = ack;
do {
SendBuf* sendbuf = GetSendqHead();
if (sendbuf->data.sendseq == ack) {
SetRexmitTimer();
rexmit_count = 1;
fastrexmit = 0;
break;
}
RemoveHeadList(&sendq);
free(sendbuf);
} while (!IsListEmpty(&sendq));
} else if (index == 0 && sent != 0) {
// Fast retransmit
if (++fastrexmit >= 2) {
XDBGTRC(MYMODNAME, "fast rexmit");
SendqXmit(GetSendqHead());
}
} else {
XDBGTRC(MYMODNAME,
"ack ignored: %u, %u, %u",
ack, serverseq_una, serverseq_nxt);
}
}
//
// Service network traffic from the client
//
VOID
ServiceClient()
{
static BOOL pending = FALSE;
static WSAOVERLAPPED overlapped;
DWORD bytesrecv, flags;
INT err;
while (TRUE) {
// Issue the overlapped recv request if necessary
if (!pending) {
WSABUF wsabuf;
wsabuf.buf = recvbuf;
wsabuf.len = sizeof(recvbuf);
flags = 0;
overlapped.hEvent = recvevt;
err = WSARecv(sock, &wsabuf, 1, &bytesrecv, &flags, &overlapped, NULL);
if (err == SOCKET_ERROR) {
err = WSAGetLastError();
if (err == WSA_IO_PENDING) {
pending = TRUE;
} else {
XDBGWRN(MYMODNAME, "failed to issue recv request: %d", err);
return;
}
}
}
// If an overlapped recv request is pending,
// check to see if it's completed yet.
if (pending) {
if (!WSAGetOverlappedResult(sock, &overlapped, &bytesrecv, FALSE, &flags)) {
err = WSAGetLastError();
if (err == WSA_IO_INCOMPLETE) return;
//
// This is very bad -
// cancel the pending recv request and try to issue a new one
//
XDBGWRN(MYMODNAME, "get overlapped result failed: %d", err);
WSACancelOverlappedIO(sock);
pending = FALSE;
continue;
}
pending = FALSE;
}
// Client must be alive again - enable retransmission
EnableRexmit();
if (bytesrecv >= sizeof(XIrmPacketHeader)) {
XIrmPacketHeader* pkt = (XIrmPacketHeader*) recvbuf;
if (pkt->sendseq != clientseq) {
XDBGTRC(MYMODNAME,
"client packet seq? %d %u, %u",
pkt->type, pkt->sendseq, clientseq);
}
switch (pkt->type) {
case PKTTYPE_ACK:
if (bytesrecv == sizeof(XIrmPacketHeader)) {
ProcessClientAck(pkt);
}
break;
case PKTTYPE_SET_STATE:
if (bytesrecv == sizeof(XIrmPacketSetState)) {
ProcessClientSetState((XIrmPacketSetState*) pkt);
}
break;
default:
XDBGTRC(MYMODNAME, "client packet type? %d", pkt->type);
break;
}
} else {
XDBGWRN(MYMODNAME, "client packet size? %d", bytesrecv);
}
}
}
//
// Wait for a client connection
//
VOID
WaitForClient()
{
struct ConnectionRequest connreq;
INT match = 0;
XDBGTRC(MYMODNAME,
"waiting for client connection: %u.%u.%u.%u %s",
clientname,
clientaddr.sin_addr.S_un.S_un_b.s_b1, clientaddr.sin_addr.S_un.S_un_b.s_b2, clientaddr.sin_addr.S_un.S_un_b.s_b3, clientaddr.sin_addr.S_un.S_un_b.s_b4);
do {
Sleep(SAMPLING_INTERVAL);
if (HasPendingConnectReq() && GetPendingConnectReq(&connreq)) {
match = MatchClient(connreq.clientname, connreq.clientaddr.sin_addr.s_addr);
if (!match) {
// the connection request wasn't valid
XDBGTRC(MYMODNAME, "client-init doesn't match");
}
}
} while (!match);
if (match == 2 && connreq.clientname[0]) {
// We have an address match and the client has a name.
// Update the config file with the client name so
// that we can do name match next time around.
strcpy(clientname, connreq.clientname);
WriteConfig();
}
InitClient(&connreq);
}
//
// Server loop
//
VOID
RunServer()
{
DWORD timer;
recvevt = WSACreateEvent();
ASSERT(recvevt != NULL);
ServiceClient();
timer = ReadTimestamp() + SAMPLING_INTERVAL;
while (TRUE) {
INT timeout = timer - ReadTimestamp();
if (timeout > 0) {
DWORD wait = WaitForSingleObject(recvevt, timeout);
if (wait == WAIT_OBJECT_0) {
// Process packets from the client
ServiceClient();
}
} else {
if (HasPendingConnectReq()) {
ProcessClientInit();
}
// Do retransmit if necessary
CheckRexmit();
// Check input state changes
PollInput();
SendDeviceChanges();
SendStateChanges();
timer = ReadTimestamp() + SAMPLING_INTERVAL;
}
}
}
//
// Client setup mode
//
VOID
DoSetup()
{
struct ConnectionRequest connreq;
XDBGTRC(MYMODNAME, "enter setup mode");
memset(clientname, 0, sizeof(clientname));
CLIENTADDR = 0;
while (TRUE) {
Sleep(SAMPLING_INTERVAL);
if (HasPendingConnectReq()) {
if (!GetPendingConnectReq(&connreq)) continue;
// If both the client and server are using autonet address,
// we'll automatically accept the client connection request.
if (MatchClient(connreq.clientname, connreq.clientaddr.sin_addr.s_addr)) {
autonetmode = TRUE;
break;
}
// Display the client name / address in the UI and
// ask the user for confirmation.
// BUGBUG - not yet implemented
XDBGWRN(MYMODNAME,
"accept connection from %s %u.%u.%u.%u?",
connreq.clientname,
connreq.clientaddr.sin_addr.S_un.S_un_b.s_b1, connreq.clientaddr.sin_addr.S_un.S_un_b.s_b2, connreq.clientaddr.sin_addr.S_un.S_un_b.s_b3, connreq.clientaddr.sin_addr.S_un.S_un_b.s_b4);
}
}
InitClient(&connreq);
}
//
// Initialize network stack
//
VOID
InitNet()
{
INT err;
WSADATA wsadata;
struct sockaddr_in sockname;
err = XNetStartup(NULL);
ASSERT(err == NO_ERROR);
err = WSAStartup(WINSOCK_VERSION, &wsadata);
ASSERT(err == NO_ERROR);
sock = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT(sock != INVALID_SOCKET);
memset(&sockname, 0, sizeof(sockname));
sockname.sin_family = AF_INET;
err = bind(sock, (struct sockaddr*) &sockname, sizeof(sockname));
ASSERT(err == NO_ERROR);
listensock = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT(listensock != INVALID_SOCKET);
sockname.sin_port = htons(XIRM_SERVER_PORT);
err = bind(listensock, (struct sockaddr*) &sockname, sizeof(sockname));
ASSERT(err == NO_ERROR);
}
//
// Server thread that listens for incoming client connection requests
//
DWORD WINAPI
ListenThreadProc(
VOID* param
)
{
CHAR buf[XIRM_MAX_PACKET_SIZE];
struct sockaddr sockname;
struct sockaddr_in* sockin;
XIrmPacketClientInit* initpkt;
INT err;
initpkt = (XIrmPacketClientInit*) buf;
memset(&sockname, 0, sizeof(sockname));
sockin = (struct sockaddr_in*) &sockname;
while (TRUE) {
INT fromlen = sizeof(sockname);
INT count = recvfrom(listensock, buf, sizeof(buf), 0, &sockname, &fromlen);
if (count != sizeof(*initpkt) || initpkt->header.type != PKTTYPE_CLIENT_INIT) {
if (count >= sizeof(XIrmPacketHeader)) {
XDBGTRC(MYMODNAME, "packet from %u.%u.%u.%u:%d discard: %d",
sockin->sin_addr.S_un.S_un_b.s_b1, sockin->sin_addr.S_un.S_un_b.s_b2, sockin->sin_addr.S_un.S_un_b.s_b3, sockin->sin_addr.S_un.S_un_b.s_b4,
ntohs(sockin->sin_port),
initpkt->header.type);
} else {
XDBGWRN(MYMODNAME, "recv error? %d, %d", count, GetLastError());
}
continue;
}
XDBGTRC(MYMODNAME,
"client-init from %u.%u.%u.%u:%d - %x %u",
sockin->sin_addr.S_un.S_un_b.s_b1, sockin->sin_addr.S_un.S_un_b.s_b2, sockin->sin_addr.S_un.S_un_b.s_b3, sockin->sin_addr.S_un.S_un_b.s_b4,
ntohs(sockin->sin_port),
initpkt->protocol_version,
initpkt->header.sendseq);
if (initpkt->protocol_version != XIRM_PROTOCOL_VERSION) {
XDBGTRC(MYMODNAME, "client version mismatch: %x %x");
continue;
}
ConnectReqLock();
connection_request.clientisn = initpkt->header.sendseq;
connection_request.clientaddr = *sockin;
memcpy(connection_request.clientname, initpkt->client_name, XIRM_MACHINE_NAME_MAXLEN-1);
ConnectReqUnlock();
}
}
void __cdecl main()
{
HANDLE thread;
DWORD tid;
InitializeListHead(&sendq);
// Initialize network stack
InitNet();
// Initialize input
InitInput();
// Create server listening thread
InitializeCriticalSection(&connection_request_lock);
thread = CreateThread(NULL, 0, ListenThreadProc, NULL, 0, &tid);
ASSERT(thread != NULL);
CloseHandle(thread);
// Check if the config file is present
if (!dosetup) {
if (!ReadConfig()) dosetup = TRUE;
}
if (dosetup) {
DoSetup();
} else {
// Server loop
WaitForClient();
}
RunServer();
Sleep(INFINITE);
}