Merge branch 'develop', read more
This merge handles a number of backend features, including error handling and other types of messages. I think there's more I could've done in this time, but this is what I have nonetheless.
This commit is contained in:
@@ -75,7 +75,7 @@ private:
|
|||||||
//base statistics
|
//base statistics
|
||||||
Statistics stats;
|
Statistics stats;
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
|
|
||||||
//metadata
|
//metadata
|
||||||
int owner;
|
int owner;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ void ClientApplication::Init(int argc, char** argv) {
|
|||||||
|
|
||||||
std::cout << "Internal sizes:" << std::endl;
|
std::cout << "Internal sizes:" << std::endl;
|
||||||
|
|
||||||
|
DEBUG_OUTPUT_VAR(NETWORK_VERSION);
|
||||||
DEBUG_OUTPUT_VAR(sizeof(Region::type_t));
|
DEBUG_OUTPUT_VAR(sizeof(Region::type_t));
|
||||||
DEBUG_OUTPUT_VAR(sizeof(Region));
|
DEBUG_OUTPUT_VAR(sizeof(Region));
|
||||||
DEBUG_OUTPUT_VAR(REGION_WIDTH);
|
DEBUG_OUTPUT_VAR(REGION_WIDTH);
|
||||||
@@ -96,8 +97,10 @@ void ClientApplication::Init(int argc, char** argv) {
|
|||||||
DEBUG_OUTPUT_VAR(REGION_DEPTH);
|
DEBUG_OUTPUT_VAR(REGION_DEPTH);
|
||||||
DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT);
|
DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT);
|
||||||
DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT);
|
DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT);
|
||||||
|
DEBUG_OUTPUT_VAR(PACKET_STRING_SIZE);
|
||||||
DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE);
|
DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE);
|
||||||
DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE);
|
DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE);
|
||||||
|
DEBUG_OUTPUT_VAR(static_cast<int>(SerialPacketType::LAST));
|
||||||
|
|
||||||
#undef DEBUG_OUTPUT_VAR
|
#undef DEBUG_OUTPUT_VAR
|
||||||
|
|
||||||
|
|||||||
@@ -85,13 +85,15 @@ void CleanUp::Update() {
|
|||||||
SetNextScene(SceneList::MAINMENU);
|
SetNextScene(SceneList::MAINMENU);
|
||||||
}
|
}
|
||||||
|
|
||||||
//BUGFIX: Eat incoming packets
|
//Eat incoming packets
|
||||||
while(network.Receive());
|
while(network.Receive());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CleanUp::Render(SDL_Surface* const screen) {
|
void CleanUp::Render(SDL_Surface* const screen) {
|
||||||
|
ConfigUtility& config = ConfigUtility::GetSingleton();
|
||||||
|
|
||||||
backButton.DrawTo(screen);
|
backButton.DrawTo(screen);
|
||||||
font.DrawStringTo("You have been disconnected.", screen, 50, 30);
|
font.DrawStringTo(config["client.disconnectMessage"], screen, 50, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------
|
//-------------------------
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ InWorld::InWorld(
|
|||||||
//TODO: Tile size and tile sheet should be loaded elsewhere
|
//TODO: Tile size and tile sheet should be loaded elsewhere
|
||||||
tileSheet.Load(config["dir.tilesets"] + "terrain.bmp", 32, 32);
|
tileSheet.Load(config["dir.tilesets"] + "terrain.bmp", 32, 32);
|
||||||
|
|
||||||
|
//send this player's character info
|
||||||
|
CharacterPacket newPacket;
|
||||||
|
newPacket.type = SerialPacketType::CHARACTER_NEW;
|
||||||
|
strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE);
|
||||||
|
strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE);
|
||||||
|
newPacket.accountIndex = accountIndex;
|
||||||
|
network.SendTo(Channels::SERVER, &newPacket);
|
||||||
|
|
||||||
//request a sync
|
//request a sync
|
||||||
RequestSynchronize();
|
RequestSynchronize();
|
||||||
|
|
||||||
@@ -142,9 +150,11 @@ void InWorld::Update() {
|
|||||||
camera.y = localCharacter->GetOrigin().y - camera.marginY;
|
camera.y = localCharacter->GetOrigin().y - camera.marginY;
|
||||||
|
|
||||||
//check the connection
|
//check the connection
|
||||||
if (Clock::now() - lastBeat > std::chrono::seconds(5)) {
|
if (Clock::now() - lastBeat > std::chrono::seconds(3)) {
|
||||||
if (attemptedBeats > 2) {
|
if (attemptedBeats > 2) {
|
||||||
throw(std::runtime_error("Connection lost"));
|
RequestDisconnect();
|
||||||
|
SetNextScene(SceneList::CLEANUP);
|
||||||
|
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server";
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerPacket newPacket;
|
ServerPacket newPacket;
|
||||||
@@ -302,6 +312,9 @@ void InWorld::HandlePacket(SerialPacket* const argPacket) {
|
|||||||
case SerialPacketType::CHARACTER_UPDATE:
|
case SerialPacketType::CHARACTER_UPDATE:
|
||||||
HandleCharacterUpdate(static_cast<CharacterPacket*>(argPacket));
|
HandleCharacterUpdate(static_cast<CharacterPacket*>(argPacket));
|
||||||
break;
|
break;
|
||||||
|
case SerialPacketType::CHARACTER_REJECTION:
|
||||||
|
HandleCharacterRejection(static_cast<TextPacket*>(argPacket));
|
||||||
|
break;
|
||||||
case SerialPacketType::REGION_CONTENT:
|
case SerialPacketType::REGION_CONTENT:
|
||||||
HandleRegionContent(static_cast<RegionPacket*>(argPacket));
|
HandleRegionContent(static_cast<RegionPacket*>(argPacket));
|
||||||
break;
|
break;
|
||||||
@@ -330,6 +343,7 @@ void InWorld::HandlePong(ServerPacket* const argPacket) {
|
|||||||
void InWorld::HandleDisconnect(ClientPacket* const argPacket) {
|
void InWorld::HandleDisconnect(ClientPacket* const argPacket) {
|
||||||
//TODO: More needed in the disconnection
|
//TODO: More needed in the disconnection
|
||||||
SetNextScene(SceneList::CLEANUP);
|
SetNextScene(SceneList::CLEANUP);
|
||||||
|
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been disconnected";
|
||||||
}
|
}
|
||||||
|
|
||||||
void InWorld::HandleCharacterNew(CharacterPacket* const argPacket) {
|
void InWorld::HandleCharacterNew(CharacterPacket* const argPacket) {
|
||||||
@@ -366,7 +380,6 @@ void InWorld::HandleCharacterNew(CharacterPacket* const argPacket) {
|
|||||||
localCharacter = &newCharacter;
|
localCharacter = &newCharacter;
|
||||||
|
|
||||||
//setup the camera
|
//setup the camera
|
||||||
//TODO: move this?
|
|
||||||
camera.width = GetScreen()->w;
|
camera.width = GetScreen()->w;
|
||||||
camera.height = GetScreen()->h;
|
camera.height = GetScreen()->h;
|
||||||
|
|
||||||
@@ -390,7 +403,6 @@ void InWorld::HandleCharacterDelete(CharacterPacket* const argPacket) {
|
|||||||
|
|
||||||
void InWorld::HandleCharacterUpdate(CharacterPacket* const argPacket) {
|
void InWorld::HandleCharacterUpdate(CharacterPacket* const argPacket) {
|
||||||
if (characterMap.find(argPacket->characterIndex) == characterMap.end()) {
|
if (characterMap.find(argPacket->characterIndex) == characterMap.end()) {
|
||||||
std::cout << "Warning: HandleCharacterUpdate() is passing to HandleCharacterNew()" << std::endl;
|
|
||||||
HandleCharacterNew(argPacket);
|
HandleCharacterNew(argPacket);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -405,6 +417,14 @@ void InWorld::HandleCharacterUpdate(CharacterPacket* const argPacket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InWorld::HandleCharacterRejection(TextPacket* const argPacket) {
|
||||||
|
RequestDisconnect();
|
||||||
|
SetNextScene(SceneList::CLEANUP);
|
||||||
|
ConfigUtility& config = ConfigUtility::GetSingleton();
|
||||||
|
config["client.disconnectMessage"] = "Error: ";
|
||||||
|
config["client.disconnectMessage"] += argPacket->text;
|
||||||
|
}
|
||||||
|
|
||||||
void InWorld::HandleRegionContent(RegionPacket* const argPacket) {
|
void InWorld::HandleRegionContent(RegionPacket* const argPacket) {
|
||||||
//replace existing regions
|
//replace existing regions
|
||||||
regionPager.UnloadRegion(argPacket->x, argPacket->y);
|
regionPager.UnloadRegion(argPacket->x, argPacket->y);
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ protected:
|
|||||||
void HandleCharacterNew(CharacterPacket* const);
|
void HandleCharacterNew(CharacterPacket* const);
|
||||||
void HandleCharacterDelete(CharacterPacket* const);
|
void HandleCharacterDelete(CharacterPacket* const);
|
||||||
void HandleCharacterUpdate(CharacterPacket* const);
|
void HandleCharacterUpdate(CharacterPacket* const);
|
||||||
|
void HandleCharacterRejection(TextPacket* const);
|
||||||
void HandleRegionContent(RegionPacket* const);
|
void HandleRegionContent(RegionPacket* const);
|
||||||
|
|
||||||
//Server control
|
//Server control
|
||||||
@@ -113,7 +114,8 @@ protected:
|
|||||||
//UI
|
//UI
|
||||||
Button disconnectButton;
|
Button disconnectButton;
|
||||||
Button shutDownButton;
|
Button shutDownButton;
|
||||||
//TODO: Review the camera
|
|
||||||
|
//the camera structure
|
||||||
struct {
|
struct {
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
int width = 0, height = 0;
|
int width = 0, height = 0;
|
||||||
@@ -125,6 +127,7 @@ protected:
|
|||||||
Character* localCharacter = nullptr;
|
Character* localCharacter = nullptr;
|
||||||
|
|
||||||
//connections
|
//connections
|
||||||
|
//TODO: This needs it's own utility, for both InWorld and InCombat
|
||||||
typedef std::chrono::steady_clock Clock;
|
typedef std::chrono::steady_clock Clock;
|
||||||
Clock::time_point lastBeat = Clock::now();
|
Clock::time_point lastBeat = Clock::now();
|
||||||
int attemptedBeats = 0;
|
int attemptedBeats = 0;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
//-------------------------
|
//-------------------------
|
||||||
//Public access members
|
//Public access members
|
||||||
@@ -63,7 +64,7 @@ LobbyMenu::LobbyMenu(int* const argClientIndex, int* const argAccountIndex):
|
|||||||
//set the server list's position
|
//set the server list's position
|
||||||
listBox = {300, 50, 200, font.GetCharH()};
|
listBox = {300, 50, 200, font.GetCharH()};
|
||||||
|
|
||||||
//BUGFIX: Eat incoming packets
|
//Eat incoming packets
|
||||||
while(network.Receive());
|
while(network.Receive());
|
||||||
|
|
||||||
//Initial broadcast
|
//Initial broadcast
|
||||||
@@ -192,6 +193,9 @@ void LobbyMenu::HandlePacket(SerialPacket* const argPacket) {
|
|||||||
case SerialPacketType::JOIN_RESPONSE:
|
case SerialPacketType::JOIN_RESPONSE:
|
||||||
HandleJoinResponse(static_cast<ClientPacket*>(argPacket));
|
HandleJoinResponse(static_cast<ClientPacket*>(argPacket));
|
||||||
break;
|
break;
|
||||||
|
case SerialPacketType::JOIN_REJECTION:
|
||||||
|
HandleJoinRejection(static_cast<TextPacket*>(argPacket));
|
||||||
|
break;
|
||||||
//handle errors
|
//handle errors
|
||||||
default:
|
default:
|
||||||
throw(std::runtime_error(std::string() + "Unknown SerialPacketType encountered in LobbyMenu: " + to_string_custom(static_cast<int>(argPacket->type)) ));
|
throw(std::runtime_error(std::string() + "Unknown SerialPacketType encountered in LobbyMenu: " + to_string_custom(static_cast<int>(argPacket->type)) ));
|
||||||
@@ -219,14 +223,11 @@ void LobbyMenu::HandleJoinResponse(ClientPacket* const argPacket) {
|
|||||||
accountIndex = argPacket->accountIndex;
|
accountIndex = argPacket->accountIndex;
|
||||||
network.Bind(argPacket->srcAddress, Channels::SERVER);
|
network.Bind(argPacket->srcAddress, Channels::SERVER);
|
||||||
SetNextScene(SceneList::INWORLD);
|
SetNextScene(SceneList::INWORLD);
|
||||||
|
}
|
||||||
|
|
||||||
//send this player's character info
|
void LobbyMenu::HandleJoinRejection(TextPacket* const argPacket) {
|
||||||
CharacterPacket newPacket;
|
//TODO: Better output
|
||||||
newPacket.type = SerialPacketType::CHARACTER_NEW;
|
std::cerr << "Error: " << argPacket->text << std::endl;
|
||||||
strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE);
|
|
||||||
strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE);
|
|
||||||
newPacket.accountIndex = accountIndex;
|
|
||||||
network.SendTo(Channels::SERVER, &newPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------
|
//-------------------------
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ protected:
|
|||||||
void HandlePacket(SerialPacket* const);
|
void HandlePacket(SerialPacket* const);
|
||||||
void HandleBroadcastResponse(ServerPacket* const);
|
void HandleBroadcastResponse(ServerPacket* const);
|
||||||
void HandleJoinResponse(ClientPacket* const);
|
void HandleJoinResponse(ClientPacket* const);
|
||||||
|
void HandleJoinRejection(TextPacket* const);
|
||||||
|
|
||||||
//server control
|
//server control
|
||||||
void SendBroadcastRequest();
|
void SendBroadcastRequest();
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void serializeCharacter(void* buffer, CharacterPacket* packet) {
|
|||||||
//stats structure
|
//stats structure
|
||||||
serializeStatistics(&buffer, &packet->stats);
|
serializeStatistics(&buffer, &packet->stats);
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserializeCharacter(void* buffer, CharacterPacket* packet) {
|
void deserializeCharacter(void* buffer, CharacterPacket* packet) {
|
||||||
@@ -70,5 +70,5 @@ void deserializeCharacter(void* buffer, CharacterPacket* packet) {
|
|||||||
//stats structure
|
//stats structure
|
||||||
deserializeStatistics(&buffer, &packet->stats);
|
deserializeStatistics(&buffer, &packet->stats);
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ struct CharacterPacket : SerialPacketBase {
|
|||||||
//gameplay
|
//gameplay
|
||||||
Statistics stats;
|
Statistics stats;
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
};
|
};
|
||||||
|
|
||||||
void serializeCharacter(void* buffer, CharacterPacket* packet);
|
void serializeCharacter(void* buffer, CharacterPacket* packet);
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/* Copyright: (c) Kayne Ruse 2013, 2014
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source
|
||||||
|
* distribution.
|
||||||
|
*/
|
||||||
|
#include "text_packet.hpp"
|
||||||
|
|
||||||
|
#include "serial_utility.hpp"
|
||||||
|
|
||||||
|
void serializeText(void* buffer, TextPacket* packet) {
|
||||||
|
serialCopy(&buffer, &packet->type, sizeof(SerialPacketType));
|
||||||
|
|
||||||
|
//content
|
||||||
|
serialCopy(&buffer, packet->name, PACKET_STRING_SIZE);
|
||||||
|
serialCopy(&buffer, packet->text, PACKET_STRING_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deserializeText(void* buffer, TextPacket* packet) {
|
||||||
|
deserialCopy(&buffer, &packet->type, sizeof(SerialPacketType));
|
||||||
|
|
||||||
|
//content
|
||||||
|
deserialCopy(&buffer, packet->name, PACKET_STRING_SIZE);
|
||||||
|
deserialCopy(&buffer, packet->text, PACKET_STRING_SIZE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/* Copyright: (c) Kayne Ruse 2013, 2014
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source
|
||||||
|
* distribution.
|
||||||
|
*/
|
||||||
|
#ifndef TEXTPACKET_HPP_
|
||||||
|
#define TEXTPACKET_HPP_
|
||||||
|
|
||||||
|
#include "serial_packet_base.hpp"
|
||||||
|
|
||||||
|
struct TextPacket : SerialPacketBase {
|
||||||
|
char name[PACKET_STRING_SIZE];
|
||||||
|
char text[PACKET_STRING_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
void serializeText(void* buffer, TextPacket* packet);
|
||||||
|
void deserializeText(void* buffer, TextPacket* packet);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -27,18 +27,20 @@
|
|||||||
#include "client_packet.hpp"
|
#include "client_packet.hpp"
|
||||||
#include "region_packet.hpp"
|
#include "region_packet.hpp"
|
||||||
#include "server_packet.hpp"
|
#include "server_packet.hpp"
|
||||||
|
#include "text_packet.hpp"
|
||||||
|
|
||||||
//SerialPacketBase is defined in serial_packet_base.hpp
|
//SerialPacketBase is defined in serial_packet_base.hpp
|
||||||
typedef SerialPacketBase SerialPacket;
|
typedef SerialPacketBase SerialPacket;
|
||||||
|
|
||||||
//DOCS: NETWORK_VERSION is used to discern compatible servers and clients
|
//DOCS: NETWORK_VERSION is used to discern compatible servers and clients
|
||||||
constexpr int NETWORK_VERSION = 20140831;
|
constexpr int NETWORK_VERSION = 20140909;
|
||||||
|
|
||||||
union MaxPacket {
|
union MaxPacket {
|
||||||
CharacterPacket a;
|
CharacterPacket a;
|
||||||
ClientPacket b;
|
ClientPacket b;
|
||||||
RegionPacket c;
|
RegionPacket c;
|
||||||
ServerPacket d;
|
ServerPacket d;
|
||||||
|
TextPacket e;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket);
|
constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket);
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ enum class SerialPacketType {
|
|||||||
//Connecting to a server as a client
|
//Connecting to a server as a client
|
||||||
JOIN_REQUEST,
|
JOIN_REQUEST,
|
||||||
JOIN_RESPONSE,
|
JOIN_RESPONSE,
|
||||||
JOIN_REJECTION,
|
|
||||||
|
|
||||||
//client requests all information from the server
|
//client requests all information from the server
|
||||||
SYNCHRONIZE,
|
SYNCHRONIZE,
|
||||||
@@ -88,10 +87,23 @@ enum class SerialPacketType {
|
|||||||
CHARACTER_STATS_REQUEST,
|
CHARACTER_STATS_REQUEST,
|
||||||
CHARACTER_STATS_RESPONSE,
|
CHARACTER_STATS_RESPONSE,
|
||||||
|
|
||||||
//reject a character request
|
//-------------------------
|
||||||
|
//TextPacket
|
||||||
|
// name, text
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
//general speech
|
||||||
|
TEXT_BROADCAST,
|
||||||
|
|
||||||
|
//rejection/error messages
|
||||||
|
SHUTDOWN_REJECTION,
|
||||||
|
JOIN_REJECTION,
|
||||||
CHARACTER_REJECTION,
|
CHARACTER_REJECTION,
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
//not used
|
//not used
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
LAST
|
LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "client_packet.hpp"
|
#include "client_packet.hpp"
|
||||||
#include "region_packet.hpp"
|
#include "region_packet.hpp"
|
||||||
#include "server_packet.hpp"
|
#include "server_packet.hpp"
|
||||||
|
#include "text_packet.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -53,7 +54,6 @@ void serializePacket(void* buffer, SerialPacketBase* packet) {
|
|||||||
break;
|
break;
|
||||||
case SerialPacketType::JOIN_REQUEST:
|
case SerialPacketType::JOIN_REQUEST:
|
||||||
case SerialPacketType::JOIN_RESPONSE:
|
case SerialPacketType::JOIN_RESPONSE:
|
||||||
case SerialPacketType::JOIN_REJECTION:
|
|
||||||
case SerialPacketType::SYNCHRONIZE:
|
case SerialPacketType::SYNCHRONIZE:
|
||||||
case SerialPacketType::DISCONNECT:
|
case SerialPacketType::DISCONNECT:
|
||||||
case SerialPacketType::SHUTDOWN:
|
case SerialPacketType::SHUTDOWN:
|
||||||
@@ -68,9 +68,14 @@ void serializePacket(void* buffer, SerialPacketBase* packet) {
|
|||||||
case SerialPacketType::CHARACTER_UPDATE:
|
case SerialPacketType::CHARACTER_UPDATE:
|
||||||
case SerialPacketType::CHARACTER_STATS_REQUEST:
|
case SerialPacketType::CHARACTER_STATS_REQUEST:
|
||||||
case SerialPacketType::CHARACTER_STATS_RESPONSE:
|
case SerialPacketType::CHARACTER_STATS_RESPONSE:
|
||||||
case SerialPacketType::CHARACTER_REJECTION:
|
|
||||||
serializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
|
serializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
|
||||||
break;
|
break;
|
||||||
|
case SerialPacketType::TEXT_BROADCAST:
|
||||||
|
case SerialPacketType::JOIN_REJECTION:
|
||||||
|
case SerialPacketType::SHUTDOWN_REJECTION:
|
||||||
|
case SerialPacketType::CHARACTER_REJECTION:
|
||||||
|
serializeText(buffer, static_cast<TextPacket*>(packet));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +93,6 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) {
|
|||||||
break;
|
break;
|
||||||
case SerialPacketType::JOIN_REQUEST:
|
case SerialPacketType::JOIN_REQUEST:
|
||||||
case SerialPacketType::JOIN_RESPONSE:
|
case SerialPacketType::JOIN_RESPONSE:
|
||||||
case SerialPacketType::JOIN_REJECTION:
|
|
||||||
case SerialPacketType::SYNCHRONIZE:
|
case SerialPacketType::SYNCHRONIZE:
|
||||||
case SerialPacketType::DISCONNECT:
|
case SerialPacketType::DISCONNECT:
|
||||||
case SerialPacketType::SHUTDOWN:
|
case SerialPacketType::SHUTDOWN:
|
||||||
@@ -103,8 +107,13 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) {
|
|||||||
case SerialPacketType::CHARACTER_UPDATE:
|
case SerialPacketType::CHARACTER_UPDATE:
|
||||||
case SerialPacketType::CHARACTER_STATS_REQUEST:
|
case SerialPacketType::CHARACTER_STATS_REQUEST:
|
||||||
case SerialPacketType::CHARACTER_STATS_RESPONSE:
|
case SerialPacketType::CHARACTER_STATS_RESPONSE:
|
||||||
case SerialPacketType::CHARACTER_REJECTION:
|
|
||||||
deserializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
|
deserializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
|
||||||
break;
|
break;
|
||||||
|
case SerialPacketType::TEXT_BROADCAST:
|
||||||
|
case SerialPacketType::JOIN_REJECTION:
|
||||||
|
case SerialPacketType::SHUTDOWN_REJECTION:
|
||||||
|
case SerialPacketType::CHARACTER_REJECTION:
|
||||||
|
deserializeText(buffer, static_cast<TextPacket*>(packet));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
//BUGFIX: memset() is used before sending a packet to remove old data; you don't want to send sensitive data over the network
|
//NOTE: memset() is used before sending a packet to remove old data; you don't want to send sensitive data over the network
|
||||||
//NOTE: don't confuse SerialPacketBase with UDPpacket
|
//NOTE: don't confuse SerialPacketBase with UDPpacket
|
||||||
|
|
||||||
void UDPNetworkUtility::Open(int port) {
|
void UDPNetworkUtility::Open(int port) {
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ ConfigUtility::table_t ConfigUtility::Read(std::string fname) {
|
|||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
//load in any subordinate config files
|
//load in any subordinate config files
|
||||||
//TODO: Possibility of nesting config levels?
|
|
||||||
if (retTable.find("config.next") != retTable.end()) {
|
if (retTable.find("config.next") != retTable.end()) {
|
||||||
table_t subTable = Read(retTable["config.next"]);
|
table_t subTable = Read(retTable["config.next"]);
|
||||||
retTable.insert(subTable.begin(), subTable.end());
|
retTable.insert(subTable.begin(), subTable.end());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ tiles = {
|
|||||||
|
|
||||||
--custom generation systems here
|
--custom generation systems here
|
||||||
function islandGenerator(region)
|
function islandGenerator(region)
|
||||||
io.write("Generating (", Region.GetX(region), ", ", Region.GetY(region), ")\n")
|
-- io.write("Generating (", Region.GetX(region), ", ", Region.GetY(region), ")\n")
|
||||||
for i = 1, Region.GetWidth(region) do
|
for i = 1, Region.GetWidth(region) do
|
||||||
for j = 1, Region.GetHeight(region) do
|
for j = 1, Region.GetHeight(region) do
|
||||||
local dist = math.dist(0, 0, i + Region.GetX(region) -1, j + Region.GetY(region) -1)
|
local dist = math.dist(0, 0, i + Region.GetX(region) -1, j + Region.GetY(region) -1)
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ int CharacterManager::LoadCharacter(int owner, std::string handle, std::string a
|
|||||||
newChar.baseStats.evasion = sqlite3_column_double(statement, 20);
|
newChar.baseStats.evasion = sqlite3_column_double(statement, 20);
|
||||||
newChar.baseStats.luck = sqlite3_column_double(statement, 21);
|
newChar.baseStats.luck = sqlite3_column_double(statement, 21);
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
|
|
||||||
//finish the routine
|
//finish the routine
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
@@ -214,7 +214,7 @@ int CharacterManager::SaveCharacter(int uid) {
|
|||||||
ret |= sqlite3_bind_double(statement, 17, character.baseStats.evasion) != SQLITE_OK;
|
ret |= sqlite3_bind_double(statement, 17, character.baseStats.evasion) != SQLITE_OK;
|
||||||
ret |= sqlite3_bind_double(statement, 18, character.baseStats.luck) != SQLITE_OK;
|
ret |= sqlite3_bind_double(statement, 18, character.baseStats.luck) != SQLITE_OK;
|
||||||
|
|
||||||
//TODO: gameplay components: equipment, items, buffs, debuffs
|
//gameplay components: equipment, items, buffs, debuffs...
|
||||||
|
|
||||||
//check for binding errors
|
//check for binding errors
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -225,7 +225,7 @@ int CharacterManager::SaveCharacter(int uid) {
|
|||||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||||
//if this fails, than something went horribly wrong
|
//if this fails, than something went horribly wrong
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
throw( std::runtime_error(std::string() + "Unknown SQL error when saving an account: " + sqlite3_errmsg(database)) );
|
throw( std::runtime_error(std::string() + "Unknown SQL error when saving a character: " + sqlite3_errmsg(database)) );
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
@@ -258,7 +258,7 @@ void CharacterManager::DeleteCharacter(int uid) {
|
|||||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||||
//if this fails, than something went horribly wrong
|
//if this fails, than something went horribly wrong
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
throw( std::runtime_error(std::string() + "Unknown SQL error when deleting an account: " + sqlite3_errmsg(database)) );
|
throw( std::runtime_error(std::string() + "Unknown SQL error when deleting a character: " + sqlite3_errmsg(database)) );
|
||||||
}
|
}
|
||||||
|
|
||||||
//finish the routine
|
//finish the routine
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ void ServerApplication::Init(int argc, char** argv) {
|
|||||||
|
|
||||||
std::cout << "Internal sizes:" << std::endl;
|
std::cout << "Internal sizes:" << std::endl;
|
||||||
|
|
||||||
|
DEBUG_OUTPUT_VAR(NETWORK_VERSION);
|
||||||
DEBUG_OUTPUT_VAR(sizeof(Region::type_t));
|
DEBUG_OUTPUT_VAR(sizeof(Region::type_t));
|
||||||
DEBUG_OUTPUT_VAR(sizeof(Region));
|
DEBUG_OUTPUT_VAR(sizeof(Region));
|
||||||
DEBUG_OUTPUT_VAR(REGION_WIDTH);
|
DEBUG_OUTPUT_VAR(REGION_WIDTH);
|
||||||
@@ -117,8 +118,10 @@ void ServerApplication::Init(int argc, char** argv) {
|
|||||||
DEBUG_OUTPUT_VAR(REGION_DEPTH);
|
DEBUG_OUTPUT_VAR(REGION_DEPTH);
|
||||||
DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT);
|
DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT);
|
||||||
DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT);
|
DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT);
|
||||||
|
DEBUG_OUTPUT_VAR(PACKET_STRING_SIZE);
|
||||||
DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE);
|
DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE);
|
||||||
DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE);
|
DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE);
|
||||||
|
DEBUG_OUTPUT_VAR(static_cast<int>(SerialPacketType::LAST));
|
||||||
|
|
||||||
#undef DEBUG_OUTPUT_VAR
|
#undef DEBUG_OUTPUT_VAR
|
||||||
|
|
||||||
@@ -148,7 +151,7 @@ void ServerApplication::Proc() {
|
|||||||
//TODO: This could be checked only every few seconds
|
//TODO: This could be checked only every few seconds
|
||||||
//Check connections
|
//Check connections
|
||||||
for (auto& it : clientMap) {
|
for (auto& it : clientMap) {
|
||||||
if (std::chrono::steady_clock::now() - it.second.GetLastBeat() > std::chrono::seconds(5)) {
|
if (std::chrono::steady_clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) {
|
||||||
ServerPacket newPacket;
|
ServerPacket newPacket;
|
||||||
newPacket.type = SerialPacketType::PING;
|
newPacket.type = SerialPacketType::PING;
|
||||||
network.SendTo(it.second.GetAddress(), &newPacket);
|
network.SendTo(it.second.GetAddress(), &newPacket);
|
||||||
|
|||||||
+42
-27
@@ -35,8 +35,6 @@ void ServerApplication::HandlePing(ServerPacket* const argPacket) {
|
|||||||
|
|
||||||
void ServerApplication::HandlePong(ServerPacket* const argPacket) {
|
void ServerApplication::HandlePong(ServerPacket* const argPacket) {
|
||||||
//find and update the specified client
|
//find and update the specified client
|
||||||
|
|
||||||
//BUGFIX: running multiple clients on one computer will result in matching host values; check the ports too
|
|
||||||
for (auto& it : clientMap) {
|
for (auto& it : clientMap) {
|
||||||
if (it.second.GetAddress().host == argPacket->srcAddress.host &&
|
if (it.second.GetAddress().host == argPacket->srcAddress.host &&
|
||||||
it.second.GetAddress().port == argPacket->srcAddress.port
|
it.second.GetAddress().port == argPacket->srcAddress.port
|
||||||
@@ -60,16 +58,18 @@ void ServerApplication::HandleBroadcastRequest(ServerPacket* const argPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) {
|
void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) {
|
||||||
//create the new client
|
|
||||||
ClientData newClient;
|
|
||||||
newClient.SetAddress(argPacket->srcAddress);
|
|
||||||
|
|
||||||
//load the user account
|
//load the user account
|
||||||
//TODO: handle passwords
|
//TODO: handle passwords
|
||||||
int accountIndex = accountMgr.LoadAccount(argPacket->username, clientIndex);
|
int accountIndex = accountMgr.LoadAccount(argPacket->username, clientIndex);
|
||||||
|
|
||||||
|
//Cannot load
|
||||||
if (accountIndex < 0) {
|
if (accountIndex < 0) {
|
||||||
//TODO: send rejection packet
|
TextPacket newPacket;
|
||||||
std::cerr << "Error: Account already loaded: " << accountIndex << std::endl;
|
newPacket.type = SerialPacketType::JOIN_REJECTION;
|
||||||
|
std::string msg = std::string() + "Account already loaded: " + argPacket->username;
|
||||||
|
memset(newPacket.name, 0, PACKET_STRING_SIZE);
|
||||||
|
strncpy(newPacket.text, msg.c_str(), PACKET_STRING_SIZE); //BUG: If the name is too long this would truncate it
|
||||||
|
network.SendTo(argPacket->srcAddress, static_cast<SerialPacket*>(&newPacket));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,10 +79,14 @@ void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) {
|
|||||||
newPacket.clientIndex = clientIndex;
|
newPacket.clientIndex = clientIndex;
|
||||||
newPacket.accountIndex = accountIndex;
|
newPacket.accountIndex = accountIndex;
|
||||||
|
|
||||||
network.SendTo(newClient.GetAddress(), static_cast<SerialPacket*>(&newPacket));
|
network.SendTo(argPacket->srcAddress, static_cast<SerialPacket*>(&newPacket));
|
||||||
|
|
||||||
|
//register the client
|
||||||
|
ClientData newClient;
|
||||||
|
newClient.SetAddress(argPacket->srcAddress);
|
||||||
|
clientMap[clientIndex++] = newClient;
|
||||||
|
|
||||||
//finished this routine
|
//finished this routine
|
||||||
clientMap[clientIndex++] = newClient;
|
|
||||||
std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl;
|
std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +109,9 @@ void ServerApplication::HandleDisconnect(ClientPacket* const argPacket) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
//save and unload this account's characters
|
//save and unload this account's characters
|
||||||
//pump the unload message to all remaining clients
|
|
||||||
characterMgr.UnloadCharacterIf([&](std::map<int, CharacterData>::iterator it) -> bool {
|
characterMgr.UnloadCharacterIf([&](std::map<int, CharacterData>::iterator it) -> bool {
|
||||||
if (argPacket->accountIndex == it->second.GetOwner()) {
|
if (argPacket->accountIndex == it->second.GetOwner()) {
|
||||||
|
//pump the unload message to all remaining clients
|
||||||
PumpCharacterUnload(it->first);
|
PumpCharacterUnload(it->first);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -167,23 +171,32 @@ void ServerApplication::HandleRegionRequest(RegionPacket* const argPacket) {
|
|||||||
|
|
||||||
void ServerApplication::HandleCharacterNew(CharacterPacket* const argPacket) {
|
void ServerApplication::HandleCharacterNew(CharacterPacket* const argPacket) {
|
||||||
//BUG: #27 Characters can be created with an invalid account index
|
//BUG: #27 Characters can be created with an invalid account index
|
||||||
|
//TODO: Make sure that a character's owner's account is loaded before continuing
|
||||||
|
|
||||||
//NOTE: misnomer, try to load the character first
|
//NOTE: misnomer, try to load the character first
|
||||||
int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar);
|
int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar);
|
||||||
|
|
||||||
|
//cannot load or create
|
||||||
|
if (characterIndex < 0) {
|
||||||
|
//build the error message
|
||||||
|
std::string msg;
|
||||||
if (characterIndex == -1) {
|
if (characterIndex == -1) {
|
||||||
//TODO: rejection packet
|
msg += "Character already loaded: ";
|
||||||
std::cerr << "Warning: Character already loaded" << std::endl;
|
}
|
||||||
|
else if (characterIndex == -2) {
|
||||||
|
msg += "Character already exists: ";
|
||||||
|
}
|
||||||
|
msg += argPacket->handle;
|
||||||
|
|
||||||
|
//create, fill and send the packet
|
||||||
|
TextPacket newPacket;
|
||||||
|
newPacket.type = SerialPacketType::CHARACTER_REJECTION;
|
||||||
|
memset(newPacket.name, 0, PACKET_STRING_SIZE);
|
||||||
|
strncpy(newPacket.text, msg.c_str(), PACKET_STRING_SIZE);
|
||||||
|
network.SendTo(argPacket->srcAddress, static_cast<SerialPacket*>(&newPacket));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (characterIndex == -2) {
|
|
||||||
//TODO: rejection packet
|
|
||||||
std::cerr << "Warning: Character already exists" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Make sure that a character's owner's account is loaded before continuing
|
|
||||||
|
|
||||||
//send this new character to all clients
|
//send this new character to all clients
|
||||||
CharacterPacket newPacket;
|
CharacterPacket newPacket;
|
||||||
newPacket.type = SerialPacketType::CHARACTER_NEW;
|
newPacket.type = SerialPacketType::CHARACTER_NEW;
|
||||||
@@ -198,9 +211,13 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket)
|
|||||||
int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar);
|
int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar);
|
||||||
|
|
||||||
//if this is not your character
|
//if this is not your character
|
||||||
if (characterIndex == -2) {
|
if (characterIndex < 0 && characterMgr.GetCharacter(characterIndex)->GetOwner() != argPacket->accountIndex) {
|
||||||
//TODO: rejection packet
|
//send the rejection packet
|
||||||
std::cerr << "Warning: Character cannot be deleted" << std::endl;
|
TextPacket newPacket;
|
||||||
|
newPacket.type = SerialPacketType::CHARACTER_REJECTION;
|
||||||
|
memset(newPacket.name, 0, PACKET_STRING_SIZE);
|
||||||
|
strncpy(newPacket.text, "Character cannot be deleted", PACKET_STRING_SIZE);
|
||||||
|
network.SendTo(argPacket->srcAddress, static_cast<SerialPacket*>(&newPacket));
|
||||||
|
|
||||||
//unload an unneeded character
|
//unload an unneeded character
|
||||||
if (characterIndex != -1) {
|
if (characterIndex != -1) {
|
||||||
@@ -223,8 +240,6 @@ void ServerApplication::HandleCharacterUpdate(CharacterPacket* const argPacket)
|
|||||||
|
|
||||||
//make a new character if this one doesn't exist
|
//make a new character if this one doesn't exist
|
||||||
if (!character) {
|
if (!character) {
|
||||||
//this isn't normal
|
|
||||||
std::cerr << "Warning: HandleCharacterUpdate() is passing to HandleCharacterNew()" << std::endl;
|
|
||||||
HandleCharacterNew(argPacket);
|
HandleCharacterNew(argPacket);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -257,7 +272,6 @@ void ServerApplication::HandleSynchronize(ClientPacket* const argPacket) {
|
|||||||
newPacket.type = SerialPacketType::CHARACTER_UPDATE;
|
newPacket.type = SerialPacketType::CHARACTER_UPDATE;
|
||||||
|
|
||||||
for (auto& it : *characterMgr.GetContainer()) {
|
for (auto& it : *characterMgr.GetContainer()) {
|
||||||
newPacket.characterIndex = it.first;
|
|
||||||
CopyCharacterToPacket(&newPacket, it.first);
|
CopyCharacterToPacket(&newPacket, it.first);
|
||||||
network.SendTo(client.GetAddress(), static_cast<SerialPacket*>(&newPacket));
|
network.SendTo(client.GetAddress(), static_cast<SerialPacket*>(&newPacket));
|
||||||
}
|
}
|
||||||
@@ -320,6 +334,7 @@ void ServerApplication::PumpPacket(SerialPacket* const argPacket) {
|
|||||||
|
|
||||||
void ServerApplication::PumpCharacterUnload(int uid) {
|
void ServerApplication::PumpCharacterUnload(int uid) {
|
||||||
//delete the client-side character(s)
|
//delete the client-side character(s)
|
||||||
|
//NOTE: This is a strange function
|
||||||
CharacterPacket newPacket;
|
CharacterPacket newPacket;
|
||||||
newPacket.type = SerialPacketType::CHARACTER_DELETE;
|
newPacket.type = SerialPacketType::CHARACTER_DELETE;
|
||||||
newPacket.characterIndex = uid;
|
newPacket.characterIndex = uid;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
TODO: Rejection messages
|
TODO: Config switch for the debug output
|
||||||
TODO: The error handling is terrible
|
TODO: A better way of handling the disconnection message
|
||||||
TODO: Move the statistics into their own SQL table, instead of duplicating the structure a dozen times
|
TODO: LobbyMenu::HandleJoinRejection()
|
||||||
TODO: Get the rooms working, even if only via hotkeys
|
TODO: Get the rooms working, even if only via hotkeys
|
||||||
TODO: Fix shoddy movement
|
TODO: Fix shoddy movement
|
||||||
|
TODO: Move the statistics into their own SQL table, instead of duplicating the structure a dozen times
|
||||||
|
|
||||||
|
TODO: Remove the big "Shut Down" button
|
||||||
|
TODO: Make a way for the server owner to control the server directly
|
||||||
TODO: Move the map system into it's own namespace?
|
TODO: Move the map system into it's own namespace?
|
||||||
TODO: The TileSheet class should implement the surface itself
|
TODO: The TileSheet class should implement the surface itself
|
||||||
TODO: make the whole thing more fault tolerant
|
TODO: make the whole thing more fault tolerant
|
||||||
|
|||||||
Reference in New Issue
Block a user