268 lines
6.4 KiB
C++
268 lines
6.4 KiB
C++
/*
|
|
ZNetClient.cpp
|
|
Author: Patrick Baggett <ptbaggett@762studios.com>
|
|
Created: 7/18/2013
|
|
|
|
Purpose:
|
|
|
|
ZNetClient -- extends ZNetHost to provide a client
|
|
|
|
License:
|
|
|
|
Copyright 2013, 762 Studios
|
|
*/
|
|
|
|
#include <ZNet/ZNetClient.hpp>
|
|
#include <ZNet/ZNetPacket.hpp>
|
|
#include <SST/SST_Assert.h>
|
|
#include <SST/SST_Endian.h>
|
|
#include <SST/SST_Time.h>
|
|
#include "ZNetPrivate.hpp"
|
|
|
|
#include <stdio.h> //HACKHACK printf
|
|
|
|
ZNetClient::ZNetClient()
|
|
{
|
|
connectedFlag = false;
|
|
}
|
|
|
|
ZNetClient::~ZNetClient()
|
|
{
|
|
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
int ZNetClient::Update()
|
|
{
|
|
bool hitError = false;
|
|
uint8_t data[9000];
|
|
|
|
//Only update if we've got a server
|
|
if(server.GetState() != STATE_UNCONNECTED)
|
|
{
|
|
SST_Socket s = server.GetSocket();
|
|
SST_NetResult res;
|
|
do
|
|
{
|
|
SST_NetAddress sender;
|
|
size_t nrReceived;
|
|
|
|
res = SST_Net_RecvFrom(s, data, sizeof(data), 0, &sender, &nrReceived);
|
|
|
|
if(res == SSTNETRESULT_SUCCESS)
|
|
{
|
|
//OK, so is the packet from the server? If not, ignore it
|
|
if(SST_Net_AddressCompare(&sender, server.GetNetAddress()) != 0)
|
|
continue;
|
|
|
|
HandlePacket(data, (uint32_t)nrReceived);
|
|
}
|
|
else if(res == SSTNETRESULT_WOULDBLOCK)
|
|
break;
|
|
else
|
|
hitError = true;
|
|
|
|
} while(res == SSTNETRESULT_SUCCESS);
|
|
}
|
|
static const char* str[] = {"UNCONNECTED", "HANDSHAKE", "CONNECTED", "SERVING"};
|
|
printf("MY STATE: %s / %u ack\n", str[server.GetState()], server.GetPacketChannel(0)->GetLocalAck());
|
|
|
|
|
|
//Send all channel data to server
|
|
if(!this->SendToPeer(&server))
|
|
hitError = true;
|
|
|
|
//Send acks
|
|
server.SendAcksForAllChannels();
|
|
server.ProcessLocalAcks();
|
|
|
|
if(hitError)
|
|
return -1;
|
|
|
|
return HasEvent() ? 1 : 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
bool ZNetClient::Connect(SST_Socket s, SST_NetAddress* addr, uint32_t nrChannels, uint32_t userData)
|
|
{
|
|
SST_OS_DebugAssert(s != 0, "Invalid socket");
|
|
SST_OS_DebugAssert(addr != NULL, "Invalid address");
|
|
SST_OS_DebugAssert(server.GetState() == STATE_UNCONNECTED, "Must be unconnected, use Reset() first");
|
|
|
|
if(nrChannels == 0 || nrChannels > UINT8_MAX)
|
|
return false;
|
|
|
|
|
|
if(!server.Initialize(this, addr, s, nrChannels))
|
|
return false;
|
|
|
|
if(SST_Net_SetNonblock(s, 1) != SSTNETRESULT_SUCCESS)
|
|
return false;
|
|
|
|
//CONNECT always occurs on ZNET_SYSCHANNEL
|
|
ZNetPacketChannel* channel = server.GetPacketChannel(ZNET_SYSCHANNEL);
|
|
|
|
userData = ZNetPrivate::ZNetByteOrder32(userData);
|
|
|
|
//Create a packet, copying the userdata
|
|
ZNetPacket* packet = this->CreatePacket((void*)&userData, sizeof(userData), 0);
|
|
if(packet == NULL)
|
|
return false;
|
|
|
|
if(!channel->QueueForSending(packet, ZNETCMD_CONNECT))
|
|
{
|
|
packet->ReleaseReference();
|
|
return false;
|
|
}
|
|
|
|
//We're done with this
|
|
packet->ReleaseReference();
|
|
|
|
//Note that we're trying!
|
|
server.SetState(STATE_HANDSHAKE);
|
|
return true;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZNetClient::HandlePacket(const uint8_t* data, uint32_t dataSize)
|
|
{
|
|
ZBinaryBufferReader reader(data, dataSize, ZNET_BYTEORDER);
|
|
static int COUNT = 0;
|
|
|
|
printf("(%u) Handling packet of %u length", ++COUNT, dataSize);
|
|
//Valid packet header?
|
|
if(!ZNetPrivate::ParseWirePacketHeader(&reader))
|
|
{
|
|
printf("...but it is invalid\n");
|
|
return;
|
|
}
|
|
|
|
ZNetPrivate::ZNetMessageContainer container;
|
|
|
|
int retval;
|
|
uint64_t now = SST_OS_GetMilliTime();
|
|
bool wasValid = true;
|
|
do
|
|
{
|
|
retval = ZNetPrivate::ReadNextMessage(&reader, &container);
|
|
printf("ReadNextMessage(): %d\n", retval);
|
|
if(retval > 0)
|
|
{
|
|
switch(container.command)
|
|
{
|
|
//Server sent how far it is. We can safely act on this now because it does not generate any events.
|
|
//ACKs themselves do not utilize sequence numbers (they always have 0)
|
|
case ZNETCMD_ACK:
|
|
{
|
|
printf("ZNETCMD_ACK: CH %u: highestRecv = %u, highestSent = %u\n", container.channel, container.parsed.ack.highestReceived, container.parsed.ack.highestSent);
|
|
//Ensure it is a valid ack
|
|
if(container.channel < server.GetNumberChannels())
|
|
{
|
|
ZNetPacketChannel* channel = server.GetPacketChannel(container.channel);
|
|
int32_t currentPing = server.GetPing();
|
|
|
|
//Update ACKs, calculating an adjusted ping
|
|
channel->UpdateRemoteAck(container.parsed.ack.highestReceived, ¤tPing);
|
|
//server.SetPing(currentPing);
|
|
server.SetPing(75);
|
|
}
|
|
else
|
|
wasValid = false;
|
|
break;
|
|
}
|
|
|
|
//Server sent data.
|
|
case ZNETCMD_DATA:
|
|
{
|
|
printf("ZNETCMD_DATA found, ch = %u, length = %u, offset = %u, maxSize = %u\n",
|
|
container.channel, container.parsed.data.length, container.parsed.data.offset,
|
|
container.parsed.data.maxSize);
|
|
if(container.channel < server.GetNumberChannels())
|
|
{
|
|
ZNetPacketChannel* ch = server.GetPacketChannel(container.channel);
|
|
|
|
ch->QueueData(&container);
|
|
}
|
|
else
|
|
wasValid = false;
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
//Server sent a connection response
|
|
case ZNETCMD_CONNRESP:
|
|
{
|
|
printf("ZNETCMD_CONNRESP: code = %u\n", container.parsed.connect.userdata);
|
|
if(container.channel == ZNET_SYSCHANNEL)
|
|
{
|
|
const uint32_t reasonCode = container.parsed.connect.userdata; //slightly less typing
|
|
|
|
//TODO: flags? can have server full!!!
|
|
|
|
|
|
//Accepted?
|
|
if(reasonCode == 0)
|
|
{
|
|
if(server.GetState() == STATE_HANDSHAKE)
|
|
{
|
|
//OK, we're good
|
|
server.SetState(STATE_CONNECTED);
|
|
|
|
ZNetEvent ev;
|
|
ev.packet = NULL;
|
|
ev.userdata = reasonCode;
|
|
ev.remote = &server;
|
|
ev.type = ZNETEVENT_CONNECT;
|
|
this->AddEvent(&ev);
|
|
}
|
|
|
|
//We got it, mark that we did.
|
|
server.GetPacketChannel(ZNET_SYSCHANNEL)->UpdateLocalAck(container.sequence);
|
|
}
|
|
else //Not accepted. In this case, the server doesn't consider us a client, so no acks required.
|
|
{
|
|
ZNetEvent ev;
|
|
|
|
ev.packet = NULL;
|
|
ev.userdata = reasonCode;
|
|
ev.remote = &server;
|
|
ev.type = ZNETEVENT_DISCONNECT;
|
|
|
|
this->AddEvent(&ev);
|
|
|
|
//TODO: clear out CONNECT command
|
|
|
|
}
|
|
} // if on system channel
|
|
else //else not accepted.
|
|
wasValid = false;
|
|
break;
|
|
}
|
|
|
|
case ZNETCMD_DISCONNECT:
|
|
SST_OS_DebugError("Not yet implemented!");
|
|
break;
|
|
|
|
//Ignore this command, it doesn't make any sense as a client
|
|
case ZNETCMD_CONNECT:
|
|
default:
|
|
wasValid = false;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
} while(retval > 0);
|
|
|
|
//If all was valid, update last received time
|
|
if(wasValid)
|
|
server.SetLastReceived(now);
|
|
|
|
|
|
|
|
|
|
} |