Initial commit
This commit is contained in:
268
ZNet/ZNetClient.cpp
Normal file
268
ZNet/ZNetClient.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user