/* ZNetClient.cpp Author: Patrick Baggett Created: 7/18/2013 Purpose: ZNetClient -- extends ZNetHost to provide a client License: Copyright 2013, 762 Studios */ #include #include #include #include #include #include "ZNetPrivate.hpp" #include //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); }