Multiple changes, read more

* Implemented HeartbeatUtility
* simplified the SQL files
* Added a dummy inventory button
This commit is contained in:
2016-04-25 22:10:11 +10:00
parent 5285397101
commit 7c6468c1dd
10 changed files with 259 additions and 145 deletions
+58
View File
@@ -0,0 +1,58 @@
/* Copyright: (c) Kayne Ruse 2013-2016
*
* 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 "heartbeat_utility.hpp"
#include "channels.hpp"
#include "ip_operators.hpp"
//heartbeat system
void HeartbeatUtility::hPing(ServerPacket* const argPacket) {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PONG;
network.SendTo(argPacket->srcAddress, &newPacket);
}
void HeartbeatUtility::hPong(ServerPacket* const argPacket) {
if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) {
throw(std::runtime_error("Heartbeat message received from an unknown source"));
}
attemptedBeats = 0;
lastBeat = Clock::now();
}
int HeartbeatUtility::CheckHeartBeat() {
//check the connection (heartbeat)
if (Clock::now() - lastBeat > std::chrono::seconds(3)) {
if (attemptedBeats > 2) {
return 1;
}
else {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PING;
network.SendTo(Channels::SERVER, &newPacket);
attemptedBeats++;
lastBeat = Clock::now();
}
}
return 0;
}
@@ -19,27 +19,24 @@
* 3. This notice may not be removed or altered from any source * 3. This notice may not be removed or altered from any source
* distribution. * distribution.
*/ */
#pragma once
------------------------- #include "server_packet.hpp"
--fill the global tables with gameplay data #include "udp_network_utility.hpp"
-------------------------
INSERT OR IGNORE INTO InventoryItems (name, type, durability) VALUES #include <chrono>
("sword", "weapon", 100),
("dagger", "weapon", 100),
("staff", "weapon", 100),
("shield", "armour", 100),
("potion", "consumable", 100)
;
--DEBUG: Test cases class HeartbeatUtility {
INSERT INTO LiveCharacters public:
//heartbeat system
void hPing(ServerPacket* const);
void hPong(ServerPacket* const);
--DEBUG: this is supposed to archive the dead characters int CheckHeartBeat();
--Insert into DeadCharacters From LiveCharacters all characters who's HP has reached zero or below
INSERT INTO DeadCharacters (uid, owner, handle, avatar, birth)
SELECT uid, owner, handle, avatar, birth FROM LiveCharacters WHERE
SELECT character FROM CharacterStatistics WHERE
SELECT uid FROM CombatStatistics WHERE health <= 0;
DELETE FROM LiveCharacters WHERE uid IN (SELECT uid FROM DeadCharacters); private:
UDPNetworkUtility& network = UDPNetworkUtility::GetSingleton();
typedef std::chrono::steady_clock Clock;
Clock::time_point lastBeat = Clock::now();
int attemptedBeats = 0;
};
+18 -41
View File
@@ -77,12 +77,16 @@ World::World(int* const argClientIndex, int* const argAccountIndex):
disconnectButton.SetText(GetRenderer(), font, WHITE, "Disconnect"); disconnectButton.SetText(GetRenderer(), font, WHITE, "Disconnect");
shutdownButton.SetBackgroundTexture(GetRenderer(), buttonImage.GetTexture()); shutdownButton.SetBackgroundTexture(GetRenderer(), buttonImage.GetTexture());
shutdownButton.SetText(GetRenderer(), font, WHITE, "Shutdown"); shutdownButton.SetText(GetRenderer(), font, WHITE, "Shutdown");
inventoryButton.SetBackgroundTexture(GetRenderer(), buttonImage.GetTexture());
inventoryButton.SetText(GetRenderer(), font, WHITE, "Inventory");
//set the button positions //set the button positions
disconnectButton.SetX(50); disconnectButton.SetX(50);
disconnectButton.SetY(50); disconnectButton.SetY(50);
shutdownButton.SetX(50); shutdownButton.SetX(50);
shutdownButton.SetY(70); shutdownButton.SetY(70);
inventoryButton.SetX(50);
inventoryButton.SetY(90);
//load the tilesheet //load the tilesheet
//TODO: (2) Tile size and tile sheet should be loaded elsewhere //TODO: (2) Tile size and tile sheet should be loaded elsewhere
@@ -161,7 +165,12 @@ void World::Update() {
delete reinterpret_cast<char*>(packetBuffer); delete reinterpret_cast<char*>(packetBuffer);
//heartbeat system //heartbeat system
CheckHeartBeat(); if (heartbeatUtility.CheckHeartBeat()) {
//escape to the disconnect screen
SendDisconnectRequest();
SetSceneSignal(SceneSignal::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server";
}
//update all entities //update all entities
for (auto& it : characterMap) { for (auto& it : characterMap) {
@@ -261,6 +270,7 @@ void World::RenderFrame(SDL_Renderer* renderer) {
//draw UI //draw UI
disconnectButton.DrawTo(renderer); disconnectButton.DrawTo(renderer);
shutdownButton.DrawTo(renderer); shutdownButton.DrawTo(renderer);
inventoryButton.DrawTo(renderer);
//FPS //FPS
fpsTextLine.DrawTo(renderer); fpsTextLine.DrawTo(renderer);
@@ -285,11 +295,13 @@ void World::QuitEvent() {
void World::MouseMotion(SDL_MouseMotionEvent const& event) { void World::MouseMotion(SDL_MouseMotionEvent const& event) {
disconnectButton.MouseMotion(event); disconnectButton.MouseMotion(event);
shutdownButton.MouseMotion(event); shutdownButton.MouseMotion(event);
inventoryButton.MouseMotion(event);
} }
void World::MouseButtonDown(SDL_MouseButtonEvent const& event) { void World::MouseButtonDown(SDL_MouseButtonEvent const& event) {
disconnectButton.MouseButtonDown(event); disconnectButton.MouseButtonDown(event);
shutdownButton.MouseButtonDown(event); shutdownButton.MouseButtonDown(event);
inventoryButton.MouseButtonDown(event);
} }
void World::MouseButtonUp(SDL_MouseButtonEvent const& event) { void World::MouseButtonUp(SDL_MouseButtonEvent const& event) {
@@ -299,6 +311,9 @@ void World::MouseButtonUp(SDL_MouseButtonEvent const& event) {
if (shutdownButton.MouseButtonUp(event) == Button::State::RELEASED) { if (shutdownButton.MouseButtonUp(event) == Button::State::RELEASED) {
SendAdminShutdownRequest(); SendAdminShutdownRequest();
} }
if (inventoryButton.MouseButtonUp(event) == Button::State::RELEASED) {
//TODO: show the inventory screen
}
} }
void World::MouseWheel(SDL_MouseWheelEvent const& event) { void World::MouseWheel(SDL_MouseWheelEvent const& event) {
@@ -411,10 +426,10 @@ void World::HandlePacket(SerialPacket* const argPacket) {
switch(argPacket->type) { switch(argPacket->type) {
//heartbeat system //heartbeat system
case SerialPacketType::PING: case SerialPacketType::PING:
hPing(static_cast<ServerPacket*>(argPacket)); heartbeatUtility.hPing(static_cast<ServerPacket*>(argPacket));
break; break;
case SerialPacketType::PONG: case SerialPacketType::PONG:
hPong(static_cast<ServerPacket*>(argPacket)); heartbeatUtility.hPong(static_cast<ServerPacket*>(argPacket));
break; break;
//game server connections //game server connections
@@ -520,44 +535,6 @@ void World::HandlePacket(SerialPacket* const argPacket) {
} }
} }
//-------------------------
//heartbeat system
//-------------------------
void World::hPing(ServerPacket* const argPacket) {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PONG;
network.SendTo(argPacket->srcAddress, &newPacket);
}
void World::hPong(ServerPacket* const argPacket) {
if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) {
throw(std::runtime_error("Heartbeat message received from an unknown source"));
}
attemptedBeats = 0;
lastBeat = Clock::now();
}
void World::CheckHeartBeat() {
//check the connection (heartbeat)
if (Clock::now() - lastBeat > std::chrono::seconds(3)) {
if (attemptedBeats > 2) {
//escape to the disconnect screen
SendDisconnectRequest();
SetSceneSignal(SceneSignal::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server";
}
else {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PING;
network.SendTo(Channels::SERVER, &newPacket);
attemptedBeats++;
lastBeat = Clock::now();
}
}
}
//------------------------- //-------------------------
//Connection control //Connection control
//------------------------- //-------------------------
+3 -9
View File
@@ -43,6 +43,7 @@
#include "base_scene.hpp" #include "base_scene.hpp"
#include "base_barrier.hpp" #include "base_barrier.hpp"
#include "base_creature.hpp" #include "base_creature.hpp"
#include "heartbeat_utility.hpp"
#include "local_character.hpp" #include "local_character.hpp"
#include "SDL2/SDL.h" #include "SDL2/SDL.h"
@@ -80,12 +81,6 @@ private:
//handle incoming traffic //handle incoming traffic
void HandlePacket(SerialPacket* const); void HandlePacket(SerialPacket* const);
//heartbeat system
void hPing(ServerPacket* const);
void hPong(ServerPacket* const);
void CheckHeartBeat();
//basic connections //basic connections
void SendLogoutRequest(); void SendLogoutRequest();
void SendDisconnectRequest(); void SendDisconnectRequest();
@@ -148,6 +143,7 @@ private:
TTF_Font* font = nullptr; TTF_Font* font = nullptr;
Button disconnectButton; Button disconnectButton;
Button shutdownButton; Button shutdownButton;
Button inventoryButton;
FrameRate fps; FrameRate fps;
TextLine fpsTextLine; TextLine fpsTextLine;
@@ -165,11 +161,9 @@ private:
LocalCharacter* localCharacter = nullptr; LocalCharacter* localCharacter = nullptr;
//heartbeat //heartbeat
//TODO: (2) Heartbeat needs it's own utility HeartbeatUtility heartbeatUtility;
typedef std::chrono::steady_clock Clock; typedef std::chrono::steady_clock Clock;
Clock::time_point lastBeat = Clock::now();
Clock::time_point queryTime = Clock::now() - std::chrono::seconds(4); //back 4 seconds to trigger automatically Clock::time_point queryTime = Clock::now() - std::chrono::seconds(4); //back 4 seconds to trigger automatically
int attemptedBeats = 0;
//ugly references; I hate this //ugly references; I hate this
ConfigUtility& config = ConfigUtility::GetSingleton(); ConfigUtility& config = ConfigUtility::GetSingleton();
+125
View File
@@ -0,0 +1,125 @@
/* Copyright: (c) Kayne Ruse 2015
*
* 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.
*/
#pragma once
//DOCS: this is a generic CSV reading tool
//DOCS: empty lines and comment lines begining with '#' are ignored
//DOCS: whitespace characters are valid field values
//DOCS: if the file is invalid, then the behavior is undefined
#include <array>
#include <cstring>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
//define the container types
template<int N>
using CSVElement = std::array<std::string, N>;
template<int N>
using CSVObject = std::vector<CSVElement<N>>;
//read a file into an object
template<int N>
CSVObject<N> readCSV(std::string fname, char delim = ',') {
//open the file
std::ifstream is(fname);
if (!is.is_open()) {
std::ostringstream msg;
msg << "Failed to open file: " << fname;
throw(std::runtime_error(msg.str()));
}
//build the scanf format
std::ostringstream format;
format << "%[^\0" << delim << "]";
//read and store each record (one per line)
CSVObject<N> object;
while(!is.eof()) {
//get a line
std::string tmpLine;
getline(is, tmpLine);
//skip blank and comment lines
if (tmpLine.size() == 0 || tmpLine[0] == '#') {
continue;
}
//read and store each field
CSVElement<N> record;
for (int i = 0; i < N; ++i) {
//get a field
char tmpField[256];
memset(tmpField, 0, 256);
sscanf(tmpLine.c_str(), format.str().c_str(), tmpField);
//prune the input
int len = std::min(strlen(tmpField)+1, tmpLine.size());
tmpLine = tmpLine.substr(len);
//store the field
record[i] = tmpField;
}
object.push_back(record);
}
//finally, close the file
is.close();
return object;
}
template<int N>
void writeCSV(std::string fname, CSVObject<N> const& object, char delim = ',') {
//open the file
std::ofstream os(fname);
if (!os.is_open()) {
std::ostringstream msg;
msg << "Failed to open file: " << fname;
throw(std::runtime_error(msg.str()));
}
//write each record, one at a time
for(auto& record : object) {
//write each field, one at a time
for (int i = 0; i < N; i++) {
os << record[i];
//print delimiter
if (i != N -1) {
os << delim;
}
}
os << std::endl;
}
//finish
os.close();
}
+5
View File
@@ -0,0 +1,5 @@
#Format (4 columns): "name", type, "sprite.png", stackable
"Sword",weapon,"sword.png",false
"Staff",weapon,"staff.png",false
"Dagger",weapon,"dagger.png",false
"Potion",consumable,"potion.png",true
Can't render this file because it contains an unexpected character in line 1 and column 22.
+32 -63
View File
@@ -57,7 +57,23 @@ CREATE TABLE IF NOT EXISTS LiveCharacters (
--physically exists in the world --physically exists in the world
roomIndex INTEGER DEFAULT 0, roomIndex INTEGER DEFAULT 0,
originX INTEGER DEFAULT 0, originX INTEGER DEFAULT 0,
originY INTEGER DEFAULT 0 originY INTEGER DEFAULT 0,
--combat stats
level INTEGER DEFAULT 0,
exp INTEGER DEFAULT 0,
maxHP INTEGER DEFAULT 0,
health INTEGER DEFAULT 0,
maxMP INTEGER DEFAULT 0,
mana INTEGER DEFAULT 0,
attack INTEGER DEFAULT 0,
defence INTEGER DEFAULT 0,
intelligence INTEGER DEFAULT 0,
resistance INTEGER DEFAULT 0,
accuracy INTEGER DEFAULT 0,
evasion INTEGER DEFAULT 0,
speed INTEGER DEFAULT 0,
luck INTEGER DEFAULT 0
); );
CREATE TABLE IF NOT EXISTS DeadCharacters ( CREATE TABLE IF NOT EXISTS DeadCharacters (
@@ -68,23 +84,21 @@ CREATE TABLE IF NOT EXISTS DeadCharacters (
handle varchar(100), handle varchar(100),
avatar varchar(100), avatar varchar(100),
birth timestamp NOT NULL, birth timestamp NOT NULL,
death timestamp NOT NULL DEFAULT (datetime()) death timestamp NOT NULL DEFAULT (datetime()),
);
CREATE TABLE IF NOT EXISTS LiveCreatures ( --combat stats
uid INTEGER PRIMARY KEY AUTOINCREMENT, level INTEGER DEFAULT 0,
exp INTEGER DEFAULT 0,
--metadata maxHP INTEGER DEFAULT 0,
handle varchar(100) UNIQUE, maxMP INTEGER DEFAULT 0,
avatar varchar(100), attack INTEGER DEFAULT 0,
defence INTEGER DEFAULT 0,
--actions intelligence INTEGER DEFAULT 0,
-- script resistance INTEGER DEFAULT 0,
accuracy INTEGER DEFAULT 0,
--physically exists in the world evasion INTEGER DEFAULT 0,
roomIndex INTEGER DEFAULT 0, speed INTEGER DEFAULT 0,
originX INTEGER DEFAULT 0, luck INTEGER DEFAULT 0
originY INTEGER DEFAULT 0
); );
------------------------- -------------------------
@@ -105,49 +119,4 @@ CREATE TABLE IF NOT EXISTS InventoryItems (
--member tables --member tables
------------------------- -------------------------
CREATE TABLE IF NOT EXISTS CombatStatistics ( --TODO
--metadata
uid INTEGER PRIMARY KEY AUTOINCREMENT,
--general use statistics
level INTEGER DEFAULT 0,
exp INTEGER DEFAULT 0,
maxHP INTEGER DEFAULT 0,
health INTEGER DEFAULT 0,
maxMP INTEGER DEFAULT 0,
mana INTEGER DEFAULT 0,
attack INTEGER DEFAULT 0,
defence INTEGER DEFAULT 0,
intelligence INTEGER DEFAULT 0,
resistance INTEGER DEFAULT 0,
accuracy REAL DEFAULT 0.0,
evasion REAL DEFAULT 0.0,
speed INTEGER DEFAULT 0,
luck REAL DEFAULT 0.0
);
-------------------------
--cross reference tables
-------------------------
CREATE TABLE IF NOT EXISTS CharacterStatistics (
character INTEGER,
statistic INTEGER,
FOREIGN KEY (character) REFERENCES LiveCharacters(uid),
FOREIGN KEY (statistic) REFERENCES CombatStatistics(uid)
);
CREATE TABLE IF NOT EXISTS CharacterItems (
character INTEGER,
item INTEGER,
FOREIGN KEY (character) REFERENCES LiveCharacters(uid),
FOREIGN KEY (item) REFERENCES InventoryItem(uid)
);
CREATE TABLE IF NOT EXISTS CharacterEquipment (
character INTEGER,
item INTEGER,
FOREIGN KEY (character) REFERENCES LiveCharacters(uid),
FOREIGN KEY (item) REFERENCES InventoryItem(uid)
);
-11
View File
@@ -21,14 +21,3 @@
*/ */
#include "inventory_manager.hpp" #include "inventory_manager.hpp"
static const char* CREATE_CHARACTER_ITEMS = "INSERT INTO CharacterItems (character, item) VALUES (?1, ?2);";
static const char* DELETE_CHARACTER_ITEMS = "DELETE FROM CharacterItems WHERE (character=?1, item=?2);";
static const char* CREATE_CHARACTER_EQUIPMENT = "INSERT INTO CharacterEquipment (character, item) VALUES (?1, ?2);";
static const char* DELETE_CHARACTER_EQUIPMENT = "DELETE FROM CharacterEquipment WHERE (character=?1, item=?2);";
int InventoryManager::CreateItem(int owner, ItemType itemType) {
sqlite3_stmt* statement = nullptr;
}
+1 -1
View File
@@ -1,5 +1,5 @@
#config #config
INCLUDES+=. .. ../characters ../creatures ../entities ../../common/gameplay ../../common/utilities INCLUDES+=. .. ../characters ../creatures ../entities ../inventory ../../common/gameplay ../../common/utilities
LIBS+= LIBS+=
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
+1 -1
View File
@@ -1,5 +1,5 @@
#config #config
INCLUDES+=. .. ../characters ../combat ../creatures ../entities ../monsters ../triggers ../../common/gameplay ../../common/map ../../common/network ../../common/network/packet_types ../../common/utilities INCLUDES+=. .. ../characters ../combat ../creatures ../entities ../inventory ../monsters ../triggers ../../common/gameplay ../../common/map ../../common/network ../../common/network/packet_types ../../common/utilities
LIBS+= LIBS+=
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))