refactor: Remove most of the multiplayer game logic
I want to come up with a better architecture.
This commit is contained in:
parent
4e1b8d8690
commit
ab02686b3c
21 changed files with 70 additions and 1216 deletions
|
@ -367,23 +367,14 @@ target_link_libraries(libgame PUBLIC libsystem)
|
|||
## libmp static library
|
||||
|
||||
set(MP_HEADERS
|
||||
src/mp/area.h
|
||||
src/mp/callbacks.h
|
||||
src/mp/command.h
|
||||
src/mp/creature.h
|
||||
src/mp/door.h
|
||||
src/mp/game.h
|
||||
src/mp/objectfactory.h
|
||||
src/mp/types.h
|
||||
src/mp/util.h)
|
||||
|
||||
set(MP_SOURCES
|
||||
src/mp/area.cpp
|
||||
src/mp/command.cpp
|
||||
src/mp/creature.cpp
|
||||
src/mp/door.cpp
|
||||
src/mp/game.cpp
|
||||
src/mp/objectfactory.cpp
|
||||
src/mp/util.cpp)
|
||||
|
||||
add_library(libmp STATIC ${MP_HEADERS} ${MP_SOURCES})
|
||||
|
|
|
@ -78,7 +78,7 @@ void Game::initObjectFactory() {
|
|||
}
|
||||
|
||||
int Game::run() {
|
||||
initSubsystems();
|
||||
init();
|
||||
loadResources();
|
||||
openMainMenu();
|
||||
configure();
|
||||
|
@ -86,12 +86,12 @@ int Game::run() {
|
|||
_window.show();
|
||||
|
||||
runMainLoop();
|
||||
deinitSubsystems();
|
||||
deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Game::initSubsystems() {
|
||||
void Game::init() {
|
||||
_window.init();
|
||||
_worldPipeline.init();
|
||||
|
||||
|
@ -496,7 +496,7 @@ void Game::onPlay(const CreatureConfiguration &config) {
|
|||
loadModule(moduleName, party);
|
||||
}
|
||||
|
||||
void Game::deinitSubsystems() {
|
||||
void Game::deinit() {
|
||||
Jobs.deinit();
|
||||
Routines.deinit();
|
||||
TheAudioPlayer.deinit();
|
||||
|
|
|
@ -93,6 +93,7 @@ protected:
|
|||
bool _pickDialogReplyEnabled { true };
|
||||
|
||||
virtual void initObjectFactory();
|
||||
virtual void init();
|
||||
virtual void configure();
|
||||
virtual void configureModule();
|
||||
virtual void update();
|
||||
|
@ -145,8 +146,7 @@ private:
|
|||
// Initialization
|
||||
|
||||
void initGameVersion();
|
||||
void initSubsystems();
|
||||
void deinitSubsystems();
|
||||
void deinit();
|
||||
|
||||
// END Initialization
|
||||
|
||||
|
|
176
src/mp/area.cpp
176
src/mp/area.cpp
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "area.h"
|
||||
|
||||
#include "../game/object/objectfactory.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
using namespace reone::net;
|
||||
using namespace reone::render;
|
||||
using namespace reone::resource;
|
||||
using namespace reone::scene;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
MultiplayerArea::MultiplayerArea(
|
||||
uint32_t id,
|
||||
GameVersion version,
|
||||
MultiplayerMode mode,
|
||||
ObjectFactory *objectFactory,
|
||||
SceneGraph *sceneGraph,
|
||||
const GraphicsOptions &opts,
|
||||
IMultiplayerCallbacks *callbacks
|
||||
) :
|
||||
Area(id, version, objectFactory, sceneGraph, opts), _callbacks(callbacks) {
|
||||
|
||||
_scriptsEnabled = mode == MultiplayerMode::Server;
|
||||
}
|
||||
|
||||
void MultiplayerArea::execute(const Command &cmd) {
|
||||
switch (cmd.type()) {
|
||||
case CommandType::LoadCreature:
|
||||
executeLoadCreature(cmd);
|
||||
break;
|
||||
case CommandType::SetPlayerRole:
|
||||
executeSetPlayerRole(cmd);
|
||||
break;
|
||||
case CommandType::SetObjectTransform:
|
||||
executeSetObjectTransform(cmd);
|
||||
break;
|
||||
case CommandType::SetObjectAnimation:
|
||||
executeSetObjectAnimation(cmd);
|
||||
break;
|
||||
case CommandType::SetCreatureMovementType:
|
||||
executeSetCreatureMovementType(cmd);
|
||||
break;
|
||||
case CommandType::SetCreatureTalking:
|
||||
executeSetCreatureTalking(cmd);
|
||||
break;
|
||||
case CommandType::SetDoorOpen:
|
||||
executeSetDoorOpen(cmd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void MultiplayerArea::executeLoadCreature(const Command &cmd) {
|
||||
shared_ptr<Creature> creature(new MultiplayerCreature(cmd.objectId(), _objectFactory, _sceneGraph, _callbacks));
|
||||
creature->setTag(cmd.tag());
|
||||
|
||||
for (auto &item : cmd.equipment()) {
|
||||
creature->equip(item);
|
||||
}
|
||||
|
||||
CreatureConfiguration config;
|
||||
config.appearance = cmd.appearance();
|
||||
|
||||
creature->load(move(config));
|
||||
creature->setPosition(cmd.position());
|
||||
creature->setHeading(cmd.heading());
|
||||
|
||||
switch (cmd.role()) {
|
||||
case CreatureRole::PartyLeader:
|
||||
_partyLeader = creature;
|
||||
break;
|
||||
case CreatureRole::PartyMember1:
|
||||
_partyMember1 = creature;
|
||||
break;
|
||||
case CreatureRole::PartyMember2:
|
||||
_partyMember2 = creature;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
landObject(*creature);
|
||||
add(creature);
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetPlayerRole(const Command &cmd) {
|
||||
switch (cmd.role()) {
|
||||
case CreatureRole::PartyLeader:
|
||||
_player = _partyLeader;
|
||||
break;
|
||||
case CreatureRole::PartyMember1:
|
||||
_player = _partyMember1;
|
||||
break;
|
||||
case CreatureRole::PartyMember2:
|
||||
_player = _partyMember2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_onPlayerChanged) {
|
||||
_onPlayerChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetObjectTransform(const Command &cmd) {
|
||||
shared_ptr<SpatialObject> object(find(cmd.objectId()));
|
||||
if (object) {
|
||||
object->setPosition(cmd.position());
|
||||
object->setHeading(cmd.heading());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetObjectAnimation(const Command &cmd) {
|
||||
shared_ptr<SpatialObject> object(find(cmd.objectId()));
|
||||
if (object) {
|
||||
object->playAnimation(cmd.animation(), cmd.animationFlags());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetCreatureMovementType(const Command &cmd) {
|
||||
shared_ptr<SpatialObject> creature(find(cmd.objectId()));
|
||||
if (creature) {
|
||||
static_cast<Creature &>(*creature).setMovementType(cmd.movementType());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetCreatureTalking(const Command &cmd) {
|
||||
shared_ptr<SpatialObject> creature(find(cmd.objectId()));
|
||||
if (creature) {
|
||||
static_cast<Creature &>(*creature).setTalking(cmd.talking());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerArea::executeSetDoorOpen(const Command &cmd) {
|
||||
shared_ptr<Object> door(find(cmd.objectId()));
|
||||
shared_ptr<Object> trigerrer(find(cmd.triggerrer()));
|
||||
if (door) {
|
||||
static_cast<Door &>(*door).open(trigerrer);
|
||||
}
|
||||
}
|
||||
|
||||
const shared_ptr<Object> MultiplayerArea::findCreatureByClientTag(const string &clientTag) const {
|
||||
auto creatures = _objectsByType.find(ObjectType::Creature)->second;
|
||||
auto it = find_if(
|
||||
creatures.begin(),
|
||||
creatures.end(),
|
||||
[this, &clientTag](const shared_ptr<Object> &o) { return static_cast<MultiplayerCreature &>(*o).clientTag() == clientTag; });
|
||||
|
||||
return it == creatures.end() ? nullptr : *it;
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../game/object/area.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "creature.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
class MultiplayerArea : public game::Area {
|
||||
public:
|
||||
MultiplayerArea(
|
||||
uint32_t id,
|
||||
resource::GameVersion version,
|
||||
MultiplayerMode mode,
|
||||
game::ObjectFactory *objectFactory,
|
||||
scene::SceneGraph *sceneGraph,
|
||||
const render::GraphicsOptions &opts,
|
||||
IMultiplayerCallbacks *callbacks);
|
||||
|
||||
void execute(const Command &cmd);
|
||||
|
||||
const std::shared_ptr<Object> findCreatureByClientTag(const std::string &clientTag) const;
|
||||
|
||||
private:
|
||||
IMultiplayerCallbacks *_callbacks { nullptr };
|
||||
|
||||
void executeLoadCreature(const Command &cmd);
|
||||
void executeSetPlayerRole(const Command &cmd);
|
||||
void executeSetObjectTransform(const Command &cmd);
|
||||
void executeSetObjectAnimation(const Command &cmd);
|
||||
void executeSetCreatureMovementType(const Command &cmd);
|
||||
void executeSetCreatureTalking(const Command &cmd);
|
||||
void executeSetDoorOpen(const Command &cmd);
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "creature.h"
|
||||
#include "door.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
class IMultiplayerCallbacks {
|
||||
public:
|
||||
virtual void onObjectTransformChanged(const game::Object &object, const glm::vec3 &position, float heading) = 0;
|
||||
virtual void onObjectAnimationChanged(const game::Object &object, const std::string &anim, int flags, float speed) = 0;
|
||||
virtual void onCreatureMovementTypeChanged(const MultiplayerCreature &creature, game::MovementType type) = 0;
|
||||
virtual void onDoorOpen(const MultiplayerDoor &door, const std::shared_ptr<game::Object> &trigerrer) = 0;
|
||||
virtual void onCreatureTalkingChanged(const MultiplayerCreature &creature, bool talking) = 0;
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -17,12 +17,12 @@
|
|||
|
||||
#include "command.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
@ -97,75 +97,10 @@ void Command::load(const ByteArray &data) {
|
|||
_type = static_cast<CommandType>(data[0]);
|
||||
|
||||
int offset = 1;
|
||||
int equipmentCount = 0;
|
||||
|
||||
_id = getUint32(data, offset);
|
||||
|
||||
switch (_type) {
|
||||
case CommandType::LoadModule:
|
||||
_module = getString(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::LoadCreature:
|
||||
_role = static_cast<CreatureRole>(getUint8(data, offset));
|
||||
_objectId = getUint16(data, offset);
|
||||
_tag = getString(data, offset);
|
||||
_appearance = getUint16(data, offset);
|
||||
_position.x = getFloat(data, offset);
|
||||
_position.y = getFloat(data, offset);
|
||||
_position.z = getFloat(data, offset);
|
||||
_heading = getFloat(data, offset);
|
||||
equipmentCount = getUint8(data, offset);
|
||||
for (int i = 0; i < equipmentCount; ++i) {
|
||||
_equipment.push_back(getString(data, offset));
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandType::SetPlayerRole:
|
||||
_role = static_cast<CreatureRole>(getUint8(data, offset));
|
||||
break;
|
||||
|
||||
case CommandType::SetObjectTransform:
|
||||
_objectId = getUint16(data, offset);
|
||||
_position.x = getFloat(data, offset);
|
||||
_position.y = getFloat(data, offset);
|
||||
_position.z = getFloat(data, offset);
|
||||
_heading = getFloat(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::SetObjectAnimation:
|
||||
_objectId = getUint16(data, offset);
|
||||
_animationFlags = getUint8(data, offset);
|
||||
_animation = getString(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::SetCreatureMovementType:
|
||||
_objectId = getUint16(data, offset);
|
||||
_movementType = static_cast<MovementType>(getUint8(data, offset));
|
||||
break;
|
||||
|
||||
case CommandType::SetCreatureTalking:
|
||||
_objectId = getUint16(data, offset);
|
||||
_talking = getUint8(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::SetDoorOpen:
|
||||
_open = getUint8(data, offset);
|
||||
_objectId = getUint16(data, offset);
|
||||
_triggerrer = getUint16(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::StartDialog:
|
||||
_resRef = getString(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::PickDialogReply:
|
||||
_replyIndex = getUint8(data, offset);
|
||||
break;
|
||||
|
||||
case CommandType::FinishDialog:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("Command: unsupported type: " + to_string(static_cast<int>(_type)));
|
||||
}
|
||||
|
@ -176,71 +111,7 @@ ByteArray Command::getBytes() const {
|
|||
putUint8(static_cast<uint8_t>(_type), data);
|
||||
putUint32(_id, data);
|
||||
|
||||
switch (static_cast<CommandType>(_type)) {
|
||||
case CommandType::LoadModule:
|
||||
putString(_module, data);
|
||||
break;
|
||||
|
||||
case CommandType::LoadCreature:
|
||||
putUint8(static_cast<uint8_t>(_role), data);
|
||||
putUint16(_objectId, data);
|
||||
putString(_tag, data);
|
||||
putUint16(_appearance, data);
|
||||
putFloat(_position.x, data);
|
||||
putFloat(_position.y, data);
|
||||
putFloat(_position.z, data);
|
||||
putFloat(_heading, data);
|
||||
putUint8(static_cast<uint8_t>(_equipment.size()), data);
|
||||
for (auto &item : _equipment) {
|
||||
putString(item, data);
|
||||
}
|
||||
break;
|
||||
|
||||
case CommandType::SetPlayerRole:
|
||||
putUint8(static_cast<uint8_t>(_role), data);
|
||||
break;
|
||||
|
||||
case CommandType::SetObjectTransform:
|
||||
putUint16(_objectId, data);
|
||||
putFloat(_position.x, data);
|
||||
putFloat(_position.y, data);
|
||||
putFloat(_position.z, data);
|
||||
putFloat(_heading, data);
|
||||
break;
|
||||
|
||||
case CommandType::SetObjectAnimation:
|
||||
putUint16(_objectId, data);
|
||||
putUint8(_animationFlags, data);
|
||||
putString(_animation, data);
|
||||
break;
|
||||
|
||||
case CommandType::SetCreatureMovementType:
|
||||
putUint16(_objectId, data);
|
||||
putUint8(static_cast<uint8_t>(_movementType), data);
|
||||
break;
|
||||
|
||||
case CommandType::SetCreatureTalking:
|
||||
putUint16(_objectId, data);
|
||||
putUint8(_talking, data);
|
||||
break;
|
||||
|
||||
case CommandType::SetDoorOpen:
|
||||
putUint8(_open, data);
|
||||
putUint16(_objectId, data);
|
||||
putUint16(_triggerrer, data);
|
||||
break;
|
||||
|
||||
case CommandType::StartDialog:
|
||||
putString(_resRef, data);
|
||||
break;
|
||||
|
||||
case CommandType::PickDialogReply:
|
||||
putUint8(_replyIndex, data);
|
||||
break;
|
||||
|
||||
case CommandType::FinishDialog:
|
||||
break;
|
||||
|
||||
switch (_type) {
|
||||
default:
|
||||
throw runtime_error("Command: unsupported type: " + to_string(static_cast<int>(_type)));
|
||||
}
|
||||
|
@ -252,70 +123,6 @@ CommandType Command::type() const {
|
|||
return _type;
|
||||
}
|
||||
|
||||
uint32_t Command::objectId() const {
|
||||
return _objectId;
|
||||
}
|
||||
|
||||
const string &Command::module() const {
|
||||
return _module;
|
||||
}
|
||||
|
||||
const string &Command::tag() const {
|
||||
return _tag;
|
||||
}
|
||||
|
||||
CreatureRole Command::role() const {
|
||||
return _role;
|
||||
}
|
||||
|
||||
int Command::appearance() const {
|
||||
return _appearance;
|
||||
}
|
||||
|
||||
const vector<string> &Command::equipment() const {
|
||||
return _equipment;
|
||||
}
|
||||
|
||||
const glm::vec3 &Command::position() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
float Command::heading() const {
|
||||
return _heading;
|
||||
}
|
||||
|
||||
const string &Command::animation() const {
|
||||
return _animation;
|
||||
}
|
||||
|
||||
int Command::animationFlags() const {
|
||||
return _animationFlags;
|
||||
}
|
||||
|
||||
MovementType Command::movementType() const {
|
||||
return _movementType;
|
||||
}
|
||||
|
||||
bool Command::talking() const {
|
||||
return _talking;
|
||||
}
|
||||
|
||||
bool Command::open() const {
|
||||
return _open;
|
||||
}
|
||||
|
||||
uint32_t Command::triggerrer() const {
|
||||
return _triggerrer;
|
||||
}
|
||||
|
||||
const string &Command::resRef() const {
|
||||
return _resRef;
|
||||
}
|
||||
|
||||
uint32_t Command::replyIndex() const {
|
||||
return _replyIndex;
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../game/types.h"
|
||||
#include "../system/types.h"
|
||||
#include "../system/net/command.h"
|
||||
|
||||
namespace reone {
|
||||
|
@ -29,17 +24,7 @@ namespace reone {
|
|||
namespace mp {
|
||||
|
||||
enum class CommandType {
|
||||
LoadModule,
|
||||
LoadCreature,
|
||||
SetPlayerRole,
|
||||
SetObjectTransform,
|
||||
SetObjectAnimation,
|
||||
SetCreatureMovementType,
|
||||
SetCreatureTalking,
|
||||
SetDoorOpen,
|
||||
StartDialog,
|
||||
PickDialogReply,
|
||||
FinishDialog
|
||||
None
|
||||
};
|
||||
|
||||
class Command : public net::Command {
|
||||
|
@ -52,44 +37,9 @@ public:
|
|||
ByteArray getBytes() const override;
|
||||
|
||||
CommandType type() const;
|
||||
uint32_t objectId() const;
|
||||
const std::string &module() const;
|
||||
const std::string &tag() const;
|
||||
game::CreatureRole role() const;
|
||||
int appearance() const;
|
||||
const std::vector<std::string> &equipment() const;
|
||||
const glm::vec3 &position() const;
|
||||
float heading() const;
|
||||
const std::string &animation() const;
|
||||
int animationFlags() const;
|
||||
game::MovementType movementType() const;
|
||||
bool talking() const;
|
||||
bool open() const;
|
||||
uint32_t triggerrer() const;
|
||||
const std::string &resRef() const;
|
||||
uint32_t replyIndex() const;
|
||||
|
||||
private:
|
||||
CommandType _type { CommandType::LoadModule };
|
||||
uint32_t _objectId { 0 };
|
||||
std::string _module;
|
||||
std::string _tag;
|
||||
game::CreatureRole _role { game::CreatureRole::None };
|
||||
int _appearance { 0 };
|
||||
std::vector<std::string> _equipment;
|
||||
glm::vec3 _position { 0.0f };
|
||||
float _heading { 0.0f };
|
||||
std::string _animation;
|
||||
int _animationFlags { 0 };
|
||||
float _animationSpeed { 1.0f };
|
||||
game::MovementType _movementType { game::MovementType::None };
|
||||
bool _talking { false };
|
||||
bool _open { false };
|
||||
uint32_t _triggerrer { 0 };
|
||||
std::string _resRef;
|
||||
uint32_t _replyIndex { 0 };
|
||||
|
||||
friend class MultiplayerGame;
|
||||
CommandType _type { CommandType::None };
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "creature.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
using namespace reone::render;
|
||||
using namespace reone::scene;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
MultiplayerCreature::MultiplayerCreature(uint32_t id, ObjectFactory *objectFactory, SceneGraph *sceneGraph, IMultiplayerCallbacks *callbacks) :
|
||||
Creature(id, objectFactory, sceneGraph), _callbacks(callbacks) {
|
||||
}
|
||||
|
||||
void MultiplayerCreature::setClientTag(const string &clientTag) {
|
||||
_clientTag = clientTag;
|
||||
}
|
||||
|
||||
bool MultiplayerCreature::isControlled() const {
|
||||
return !_clientTag.empty();
|
||||
}
|
||||
|
||||
const string &MultiplayerCreature::clientTag() const {
|
||||
return _clientTag;
|
||||
}
|
||||
|
||||
void MultiplayerCreature::playAnimation(const string &anim, int flags, float speed) {
|
||||
SpatialObject::playAnimation(anim, flags);
|
||||
_callbacks->onObjectAnimationChanged(*this, anim, flags, speed);
|
||||
}
|
||||
|
||||
void MultiplayerCreature::updateTransform() {
|
||||
SpatialObject::updateTransform();
|
||||
_callbacks->onObjectTransformChanged(*this, _position, _heading);
|
||||
}
|
||||
|
||||
void MultiplayerCreature::setMovementType(MovementType type) {
|
||||
if (type == _movementType) return;
|
||||
|
||||
Creature::setMovementType(type);
|
||||
_callbacks->onCreatureMovementTypeChanged(*this, type);
|
||||
}
|
||||
|
||||
void MultiplayerCreature::setTalking(bool talking) {
|
||||
if (talking == _talking) return;
|
||||
|
||||
Creature::setTalking(talking);
|
||||
_callbacks->onCreatureTalkingChanged(*this, talking);
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../game/object/creature.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
class IMultiplayerCallbacks;
|
||||
|
||||
class MultiplayerCreature : public game::Creature {
|
||||
public:
|
||||
MultiplayerCreature(
|
||||
uint32_t id,
|
||||
game::ObjectFactory *objectFactory,
|
||||
scene::SceneGraph *sceneGraph,
|
||||
IMultiplayerCallbacks *callbacks);
|
||||
|
||||
void setClientTag(const std::string &clientTag);
|
||||
|
||||
bool isControlled() const;
|
||||
const std::string &clientTag() const;
|
||||
|
||||
private:
|
||||
IMultiplayerCallbacks *_callbacks { nullptr };
|
||||
std::string _clientTag;
|
||||
|
||||
void playAnimation(const std::string &name, int flags, float speed) override;
|
||||
void updateTransform() override;
|
||||
void setMovementType(game::MovementType type) override;
|
||||
void setTalking(bool talking) override;
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "door.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
using namespace reone::scene;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
MultiplayerDoor::MultiplayerDoor(uint32_t id, SceneGraph *sceneGraph, IMultiplayerCallbacks *callbacks) :
|
||||
Door(id, sceneGraph), _callbacks(callbacks) {
|
||||
}
|
||||
|
||||
void MultiplayerDoor::open(const shared_ptr<Object> &trigerrer) {
|
||||
Door::open(trigerrer);
|
||||
_callbacks->onDoorOpen(*this, trigerrer);
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../game/object/door.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
class IMultiplayerCallbacks;
|
||||
|
||||
class MultiplayerDoor : public game::Door {
|
||||
public:
|
||||
MultiplayerDoor(uint32_t id, scene::SceneGraph *sceneGraph, IMultiplayerCallbacks *callbacks);
|
||||
|
||||
void open(const std::shared_ptr<game::Object> &trigerrer) override;
|
||||
|
||||
private:
|
||||
IMultiplayerCallbacks *_callbacks { nullptr };;
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
299
src/mp/game.cpp
299
src/mp/game.cpp
|
@ -19,8 +19,6 @@
|
|||
|
||||
#include "../system/log.h"
|
||||
|
||||
#include "area.h"
|
||||
#include "objectfactory.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -38,15 +36,9 @@ namespace mp {
|
|||
|
||||
MultiplayerGame::MultiplayerGame(MultiplayerMode mode, const fs::path &path, const Options &opts) :
|
||||
Game(path, opts), _mode(mode) {
|
||||
|
||||
_pickDialogReplyEnabled = _mode == MultiplayerMode::Server;
|
||||
}
|
||||
|
||||
void MultiplayerGame::initObjectFactory() {
|
||||
_objectFactory = unique_ptr<ObjectFactory>(new MultiplayerObjectFactory(_version, _mode, &_sceneGraph, this, _options.graphics));
|
||||
}
|
||||
|
||||
void MultiplayerGame::configure() {
|
||||
void MultiplayerGame::init() {
|
||||
switch (_mode) {
|
||||
case MultiplayerMode::Server:
|
||||
_server = make_unique<Server>();
|
||||
|
@ -54,112 +46,22 @@ void MultiplayerGame::configure() {
|
|||
_server->setOnClientDisconnected(bind(&MultiplayerGame::onClientDisconnected, this, _1));
|
||||
_server->setOnCommandReceived(bind(&MultiplayerGame::onCommandReceived, this, _2));
|
||||
_server->start(_options.network.port);
|
||||
Game::configure();
|
||||
break;
|
||||
|
||||
case MultiplayerMode::Client:
|
||||
_client.reset(new Client());
|
||||
_client->setOnCommandReceived(bind(&MultiplayerGame::onCommandReceived, this, _1));
|
||||
_client->start(_options.network.host, _options.network.port);
|
||||
break;
|
||||
|
||||
default:
|
||||
Game::configure();
|
||||
break;
|
||||
}
|
||||
Game::init();
|
||||
}
|
||||
|
||||
void MultiplayerGame::onClientConnected(const string tag) {
|
||||
synchronizeClient(tag);
|
||||
}
|
||||
|
||||
void MultiplayerGame::synchronizeClient(const string &tag) {
|
||||
lock_guard<recursive_mutex> syncLock(_syncMutex);
|
||||
sendLoadModule(tag, _module->name());
|
||||
|
||||
shared_ptr<Object> partyLeader(_module->area()->partyLeader());
|
||||
sendLoadCreature(tag, CreatureRole::PartyLeader, static_cast<Creature &>(*partyLeader));
|
||||
|
||||
shared_ptr<Object> partyMember1(_module->area()->partyMember1());
|
||||
if (partyMember1) {
|
||||
Creature &creature = static_cast<Creature &>(*partyMember1);
|
||||
sendLoadCreature(tag, CreatureRole::PartyMember1, creature);
|
||||
}
|
||||
|
||||
shared_ptr<Object> partyMember2(_module->area()->partyMember2());
|
||||
if (partyMember2) {
|
||||
Creature &creature = static_cast<Creature &>(*partyMember2);
|
||||
sendLoadCreature(tag, CreatureRole::PartyMember2, creature);
|
||||
}
|
||||
|
||||
bool control = false;
|
||||
if (partyMember1) {
|
||||
MultiplayerCreature &creature = static_cast<MultiplayerCreature &>(*partyMember1);
|
||||
if (!creature.isControlled()) {
|
||||
creature.setClientTag(tag);
|
||||
sendSetPlayerRole(tag, CreatureRole::PartyMember1);
|
||||
control = true;
|
||||
}
|
||||
}
|
||||
if (!control && partyMember2) {
|
||||
MultiplayerCreature &creature = static_cast<MultiplayerCreature &>(*partyMember2);
|
||||
if (!creature.isControlled()) {
|
||||
creature.setClientTag(tag);
|
||||
sendSetPlayerRole(tag, CreatureRole::PartyMember2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendLoadModule(const string &client, const string &module) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::LoadModule));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._module = module;
|
||||
|
||||
sendCommand(client, cmd);
|
||||
}
|
||||
|
||||
shared_ptr<net::Command> MultiplayerGame::makeCommand(CommandType type) {
|
||||
return shared_ptr<net::Command>(new Command(_cmdCounter++, type));
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendCommand(const string &client, const shared_ptr<net::Command> &command) {
|
||||
_server->send(client, command);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendLoadCreature(const string &client, CreatureRole role, const Creature &creature) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::LoadCreature));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._role = role;
|
||||
cmd2._objectId = creature.id();
|
||||
cmd2._tag = creature.tag();
|
||||
cmd2._appearance = creature.appearance();
|
||||
cmd2._position = creature.position();
|
||||
cmd2._heading = creature.heading();
|
||||
cmd2._equipment.clear();
|
||||
|
||||
for (auto &pair : creature.equipment()) {
|
||||
cmd2._equipment.push_back(pair.second->blueprint().resRef());
|
||||
}
|
||||
|
||||
sendCommand(client, cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetPlayerRole(const string &client, CreatureRole role) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetPlayerRole));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._role = role;
|
||||
|
||||
sendCommand(client, cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::onClientDisconnected(const string tag) {
|
||||
if (!_module || !_module->loaded()) return;
|
||||
|
||||
shared_ptr<Area> area(_module->area());
|
||||
shared_ptr<Object> object(static_cast<MultiplayerArea &>(*area).findCreatureByClientTag(tag));
|
||||
if (object) {
|
||||
static_cast<MultiplayerCreature &>(*object).setClientTag("");
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::onCommandReceived(const ByteArray &data) {
|
||||
|
@ -173,94 +75,27 @@ void MultiplayerGame::onCommandReceived(const ByteArray &data) {
|
|||
}
|
||||
|
||||
void MultiplayerGame::update() {
|
||||
lock_guard<recursive_mutex> lock(_commandsInMutex);
|
||||
bool skip = false;
|
||||
|
||||
while (!_commandsIn.empty() && !skip) {
|
||||
const Command &cmd = _commandsIn.front();
|
||||
switch (cmd.type()) {
|
||||
case CommandType::LoadModule:
|
||||
if (!_module || _module->name() != cmd.module()) {
|
||||
_nextModule = cmd.module();
|
||||
}
|
||||
_commandsIn.pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_module && _nextModule.empty()) {
|
||||
switch (cmd.type()) {
|
||||
case CommandType::StartDialog: {
|
||||
shared_ptr<SpatialObject> object(_module->area()->find(cmd.objectId()));
|
||||
startDialog(*object, cmd.resRef());
|
||||
break;
|
||||
}
|
||||
case CommandType::PickDialogReply:
|
||||
_dialogGui->pickReply(cmd.replyIndex());
|
||||
break;
|
||||
case CommandType::FinishDialog:
|
||||
_screen = GameScreen::InGame;
|
||||
break;
|
||||
default:
|
||||
static_cast<MultiplayerArea &>(*_module->area()).execute(cmd);
|
||||
break;
|
||||
}
|
||||
_commandsIn.pop();
|
||||
} else {
|
||||
skip = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
processCommands();
|
||||
Game::update();
|
||||
}
|
||||
|
||||
void MultiplayerGame::loadNextModule() {
|
||||
Game::loadNextModule();
|
||||
|
||||
if (_mode == MultiplayerMode::Server) {
|
||||
for (auto &client : _server->clients()) {
|
||||
synchronizeClient(client.first);
|
||||
}
|
||||
void MultiplayerGame::processCommands() {
|
||||
lock_guard<recursive_mutex> lock(_commandsInMutex);
|
||||
while (!_commandsIn.empty()) {
|
||||
const Command &cmd = _commandsIn.front();
|
||||
_commandsIn.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::onObjectTransformChanged(const Object &object, const glm::vec3 &position, float heading) {
|
||||
if (shouldSendObjectUpdates(object.id())) {
|
||||
sendSetObjectTransform(object.id(), position, heading);
|
||||
}
|
||||
unique_ptr<net::Command> MultiplayerGame::newCommand(CommandType type) {
|
||||
return make_unique<Command>(_cmdCounter++, type);
|
||||
}
|
||||
|
||||
bool MultiplayerGame::shouldSendObjectUpdates(uint32_t objectId) const {
|
||||
if (!_module || !_module->loaded()) return false;
|
||||
|
||||
shared_ptr<Object> player(_module->area()->player());
|
||||
|
||||
switch (_mode) {
|
||||
case MultiplayerMode::Server:
|
||||
return true;
|
||||
|
||||
case MultiplayerMode::Client:
|
||||
return player && objectId == player->id();
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
void MultiplayerGame::send(const string &client, const shared_ptr<net::Command> &command) {
|
||||
_server->send(client, command);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetObjectTransform(uint32_t objectId, const glm::vec3 &position, float heading) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetObjectTransform));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = objectId;
|
||||
cmd2._position = position;
|
||||
cmd2._heading = heading;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendCommand(const shared_ptr<net::Command> &command) {
|
||||
void MultiplayerGame::send(const shared_ptr<net::Command> &command) {
|
||||
switch (_mode) {
|
||||
case MultiplayerMode::Server:
|
||||
_server->sendToAll(command);
|
||||
|
@ -271,114 +106,6 @@ void MultiplayerGame::sendCommand(const shared_ptr<net::Command> &command) {
|
|||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::onObjectAnimationChanged(const Object &object, const string &anim, int flags, float speed) {
|
||||
if (shouldSendObjectUpdates(object.id())) {
|
||||
sendSetObjectAnimation(object.id(), anim, flags, speed);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetObjectAnimation(uint32_t objectId, const string &animation, int flags, float speed) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetObjectAnimation));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = objectId;
|
||||
cmd2._animation = animation;
|
||||
cmd2._animationFlags = flags;
|
||||
cmd2._animationSpeed = speed;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::onCreatureMovementTypeChanged(const MultiplayerCreature &creature, MovementType type) {
|
||||
if (shouldSendObjectUpdates(creature.id())) {
|
||||
sendSetCreatureMovementType(creature.id(), type);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::onCreatureTalkingChanged(const MultiplayerCreature &creature, bool talking) {
|
||||
if (shouldSendObjectUpdates(creature.id())) {
|
||||
sendSetCreatureTalking(creature.id(), talking);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::startDialog(SpatialObject &owner, const string &resRef) {
|
||||
Game::startDialog(owner, resRef);
|
||||
|
||||
if (_mode == MultiplayerMode::Server) {
|
||||
sendStartDialog(owner.id(), resRef);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::onDialogReplyPicked(uint32_t index) {
|
||||
if (_mode == MultiplayerMode::Client) return;
|
||||
|
||||
Game::onDialogReplyPicked(index);
|
||||
sendPickDialogReply(index);
|
||||
}
|
||||
|
||||
void MultiplayerGame::onDialogFinished() {
|
||||
if (_mode == MultiplayerMode::Client) return;
|
||||
|
||||
Game::onDialogFinished();
|
||||
sendFinishDialog();
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetCreatureMovementType(uint32_t objectId, MovementType type) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetCreatureMovementType));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = objectId;
|
||||
cmd2._movementType = type;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetCreatureTalking(uint32_t objectId, bool talking) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetCreatureTalking));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = objectId;
|
||||
cmd2._talking = talking;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::onDoorOpen(const MultiplayerDoor &door, const shared_ptr<Object> &trigerrer) {
|
||||
uint32_t triggerrerId(trigerrer ? trigerrer->id() : 0);
|
||||
if (shouldSendObjectUpdates(triggerrerId)) {
|
||||
sendSetDoorOpen(door.id(), triggerrerId);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendSetDoorOpen(uint32_t objectId, uint32_t triggerrer) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::SetDoorOpen));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = objectId;
|
||||
cmd2._triggerrer = triggerrer;
|
||||
cmd2._open = true;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendStartDialog(uint32_t ownerId, const string &resRef) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::StartDialog));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._objectId = ownerId;
|
||||
cmd2._resRef = resRef;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendPickDialogReply(uint32_t index) {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::PickDialogReply));
|
||||
Command &cmd2 = static_cast<Command &>(*cmd);
|
||||
cmd2._replyIndex = index;
|
||||
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
void MultiplayerGame::sendFinishDialog() {
|
||||
shared_ptr<net::Command> cmd(makeCommand(CommandType::FinishDialog));
|
||||
sendCommand(cmd);
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
||||
|
|
|
@ -17,15 +17,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "../game/game.h"
|
||||
#include "../system/net/client.h"
|
||||
#include "../system/net/server.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "command.h"
|
||||
#include "types.h"
|
||||
|
||||
|
@ -33,7 +30,7 @@ namespace reone {
|
|||
|
||||
namespace mp {
|
||||
|
||||
class MultiplayerGame : public game::Game, private IMultiplayerCallbacks {
|
||||
class MultiplayerGame : public game::Game {
|
||||
public:
|
||||
MultiplayerGame(
|
||||
MultiplayerMode mode,
|
||||
|
@ -41,11 +38,10 @@ public:
|
|||
const game::Options &opts);
|
||||
|
||||
private:
|
||||
uint32_t _cmdCounter { 0 };
|
||||
MultiplayerMode _mode { MultiplayerMode::Server };
|
||||
std::recursive_mutex _syncMutex;
|
||||
std::unique_ptr<net::Client> _client;
|
||||
std::unique_ptr<net::Server> _server;
|
||||
std::unique_ptr<net::Client> _client;
|
||||
uint32_t _cmdCounter { 0 };
|
||||
|
||||
// Commands
|
||||
|
||||
|
@ -54,50 +50,21 @@ private:
|
|||
|
||||
// END Commands
|
||||
|
||||
// Game overrides
|
||||
|
||||
void initObjectFactory() override;
|
||||
void configure() override;
|
||||
void init() override;
|
||||
void update() override;
|
||||
void loadNextModule() override;
|
||||
void startDialog(game::SpatialObject &owner, const std::string &resRef) override;
|
||||
void onDialogReplyPicked(uint32_t index) override;
|
||||
void onDialogFinished() override;
|
||||
|
||||
// END Game overrides
|
||||
void processCommands();
|
||||
std::unique_ptr<net::Command> newCommand(CommandType type);
|
||||
void send(const std::shared_ptr<net::Command> &command);
|
||||
void send(const std::string &client, const std::shared_ptr<net::Command> &command);
|
||||
|
||||
// IMultiplayerCallbacks overrides
|
||||
|
||||
void onObjectTransformChanged(const game::Object &object, const glm::vec3 &position, float heading) override;
|
||||
void onObjectAnimationChanged(const game::Object &object, const std::string &anim, int flags, float speed) override;
|
||||
void onCreatureMovementTypeChanged(const MultiplayerCreature &creature, game::MovementType type) override;
|
||||
void onCreatureTalkingChanged(const MultiplayerCreature &creature, bool talking) override;
|
||||
|
||||
// END IMultiplayerCallbacks overrides
|
||||
|
||||
std::shared_ptr<net::Command> makeCommand(CommandType type);
|
||||
bool shouldSendObjectUpdates(uint32_t objectId) const;
|
||||
void synchronizeClient(const std::string &tag);
|
||||
|
||||
void sendLoadModule(const std::string &client, const std::string &module);
|
||||
void sendLoadCreature(const std::string &client, game::CreatureRole role, const game::Creature &creature);
|
||||
void sendSetPlayerRole(const std::string &client, game::CreatureRole role);
|
||||
void sendSetObjectTransform(uint32_t objectId, const glm::vec3 &position, float heading);
|
||||
void sendSetObjectAnimation(uint32_t objectId, const std::string &animation, int flags, float speed);
|
||||
void sendSetCreatureMovementType(uint32_t objectId, game::MovementType type);
|
||||
void sendSetCreatureTalking(uint32_t objectId, bool talking);
|
||||
void sendSetDoorOpen(uint32_t objectId, uint32_t triggerrer);
|
||||
void sendStartDialog(uint32_t ownerId, const std::string &resRef);
|
||||
void sendPickDialogReply(uint32_t index);
|
||||
void sendFinishDialog();
|
||||
|
||||
void sendCommand(const std::shared_ptr<net::Command> &command);
|
||||
void sendCommand(const std::string &client, const std::shared_ptr<net::Command> &command);
|
||||
// Event handlers
|
||||
|
||||
void onClientConnected(const std::string tag);
|
||||
void onClientDisconnected(const std::string tag);
|
||||
void onCommandReceived(const ByteArray &data);
|
||||
void onDoorOpen(const MultiplayerDoor &door, const std::shared_ptr<game::Object> &trigerrer) override;
|
||||
|
||||
// END Event handlers
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "objectfactory.h"
|
||||
|
||||
#include "area.h"
|
||||
#include "creature.h"
|
||||
#include "door.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
using namespace reone::render;
|
||||
using namespace reone::resource;
|
||||
using namespace reone::scene;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
MultiplayerObjectFactory::MultiplayerObjectFactory(
|
||||
GameVersion version,
|
||||
MultiplayerMode mode,
|
||||
SceneGraph *sceneGraph,
|
||||
IMultiplayerCallbacks *callbacks,
|
||||
const GraphicsOptions &opts
|
||||
) :
|
||||
ObjectFactory(version, sceneGraph, opts), _mode(mode), _callbacks(callbacks) {
|
||||
}
|
||||
|
||||
unique_ptr<Area> MultiplayerObjectFactory::newArea() {
|
||||
return make_unique<MultiplayerArea>(_counter++, _version, _mode, this, _sceneGraph, _options, _callbacks);
|
||||
}
|
||||
|
||||
unique_ptr<Creature> MultiplayerObjectFactory::newCreature() {
|
||||
return make_unique<MultiplayerCreature>(_counter++, this, _sceneGraph, _callbacks);
|
||||
}
|
||||
|
||||
unique_ptr<Door> MultiplayerObjectFactory::newDoor() {
|
||||
return make_unique<MultiplayerDoor>(_counter++, _sceneGraph, _callbacks);
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2020 Vsevolod Kremianskii
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../game/object/objectfactory.h"
|
||||
|
||||
#include "callbacks.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
class MultiplayerObjectFactory : public game::ObjectFactory {
|
||||
public:
|
||||
MultiplayerObjectFactory(
|
||||
resource::GameVersion version,
|
||||
MultiplayerMode mode,
|
||||
scene::SceneGraph *sceneGraph,
|
||||
IMultiplayerCallbacks *callbacks,
|
||||
const render::GraphicsOptions &opts);
|
||||
|
||||
std::unique_ptr<game::Area> newArea();
|
||||
std::unique_ptr<game::Creature> newCreature();
|
||||
std::unique_ptr<game::Door> newDoor();
|
||||
|
||||
private:
|
||||
MultiplayerMode _mode { MultiplayerMode::None };
|
||||
IMultiplayerCallbacks *_callbacks { nullptr };
|
||||
};
|
||||
|
||||
} // namespace mp
|
||||
|
||||
} // namespace reone
|
|
@ -23,35 +23,20 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::game;
|
||||
using namespace reone::net;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace mp {
|
||||
|
||||
static map<CommandType, string> g_cmdDesc = {
|
||||
{ CommandType::LoadModule, "LoadModule" },
|
||||
{ CommandType::LoadCreature, "LoadCreature" },
|
||||
{ CommandType::SetPlayerRole, "SetPlayerRole" },
|
||||
{ CommandType::SetObjectTransform, "SetObjectTransform" },
|
||||
{ CommandType::SetObjectAnimation, "SetObjectAnimation" },
|
||||
{ CommandType::SetCreatureMovementType, "SetCreatureMovementType" },
|
||||
{ CommandType::SetCreatureTalking, "SetCreatureTalking" },
|
||||
{ CommandType::SetDoorOpen, "SetDoorOpen" },
|
||||
{ CommandType::StartDialog, "StartDialog" },
|
||||
{ CommandType::PickDialogReply, "PickDialogReply" },
|
||||
{ CommandType::FinishDialog, "FinishDialog" }
|
||||
};
|
||||
|
||||
static const string &describeCommandType(CommandType type) {
|
||||
auto desc = g_cmdDesc.find(type);
|
||||
if (desc == g_cmdDesc.end()) {
|
||||
auto pair = g_cmdDesc.insert(make_pair(type, to_string(static_cast<int>(type))));
|
||||
return pair.first->second;
|
||||
static map<CommandType, string> descriptions;
|
||||
|
||||
auto maybeDescription = descriptions.find(type);
|
||||
if (maybeDescription != descriptions.end()) {
|
||||
return maybeDescription->second;
|
||||
}
|
||||
|
||||
return desc->second;
|
||||
auto inserted = descriptions.insert(make_pair(type, to_string(static_cast<int>(type))));
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
string describeCommand(const Command &command) {
|
||||
|
|
|
@ -134,7 +134,9 @@ void Connection::handleWrite(uint32_t commandId, shared_ptr<boost::asio::streamb
|
|||
|
||||
if (ec) {
|
||||
error("Connection: write failed: " + ec.message());
|
||||
if (_onAbort) _onAbort(_tag);
|
||||
if (_onAbort) {
|
||||
_onAbort(_tag);
|
||||
}
|
||||
}
|
||||
lock_guard<recursive_mutex> lock(_cmdOutMutex);
|
||||
auto command = find_if(
|
||||
|
@ -150,14 +152,14 @@ void Connection::handleWrite(uint32_t commandId, shared_ptr<boost::asio::streamb
|
|||
}
|
||||
}
|
||||
|
||||
void Connection::setTag(const string &tag) {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
const string &Connection::tag() const {
|
||||
return _tag;
|
||||
}
|
||||
|
||||
void Connection::setTag(const string &tag) {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
void Connection::setOnAbort(const function<void(const string &)> &fn) {
|
||||
_onAbort = fn;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
|
@ -44,23 +44,32 @@ public:
|
|||
|
||||
void send(const std::shared_ptr<Command> &command);
|
||||
|
||||
const std::string &tag() const;
|
||||
|
||||
void setTag(const std::string &tag);
|
||||
|
||||
const std::string &tag() const;
|
||||
// Callbacks
|
||||
|
||||
void setOnAbort(const std::function<void(const std::string &)> &fn);
|
||||
void setOnCommandReceived(const std::function<void(const ByteArray &)> &fn);
|
||||
|
||||
// END Callbacks
|
||||
|
||||
private:
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> _socket;
|
||||
std::string _tag;
|
||||
boost::asio::streambuf _readBuffer;
|
||||
int _cmdLength { 0 };
|
||||
std::list<std::shared_ptr<Command>> _cmdOut;
|
||||
std::vector<std::shared_ptr<Command>> _cmdOut;
|
||||
std::recursive_mutex _cmdOutMutex;
|
||||
|
||||
// Callbacks
|
||||
|
||||
std::function<void(const std::string &)> _onAbort;
|
||||
std::function<void(const ByteArray &)> _onCommandReceived;
|
||||
|
||||
// END Callbacks
|
||||
|
||||
Connection(const Connection &) = delete;
|
||||
Connection &operator=(const Connection &) = delete;
|
||||
|
||||
|
|
|
@ -110,12 +110,12 @@ void Server::stop() {
|
|||
}
|
||||
|
||||
void Server::send(const string &tag, const shared_ptr<Command> &command) {
|
||||
auto it = _clients.find(tag);
|
||||
if (it == _clients.end()) {
|
||||
auto maybeClient = _clients.find(tag);
|
||||
if (maybeClient == _clients.end()) {
|
||||
warn("TCP: invalid client: " + tag);
|
||||
return;
|
||||
}
|
||||
it->second->send(command);
|
||||
maybeClient->second->send(command);
|
||||
}
|
||||
|
||||
void Server::sendToAll(const shared_ptr<Command> &command) {
|
||||
|
|
|
@ -48,10 +48,13 @@ public:
|
|||
const ServerClients &clients() const;
|
||||
|
||||
// Callbacks
|
||||
|
||||
void setOnClientConnected(const std::function<void(const std::string &)> &fn);
|
||||
void setOnClientDisconnected(const std::function<void(const std::string &)> &fn);
|
||||
void setOnCommandReceived(const std::function<void(const std::string &, const ByteArray &)> &fn);
|
||||
|
||||
// END Callbacks
|
||||
|
||||
private:
|
||||
boost::asio::io_service _service;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> _acceptor;
|
||||
|
@ -59,10 +62,13 @@ private:
|
|||
ServerClients _clients;
|
||||
|
||||
// Callbacks
|
||||
|
||||
std::function<void(const std::string &)> _onClientConnected;
|
||||
std::function<void(const std::string &)> _onClientDisconnected;
|
||||
std::function<void(const std::string &, const ByteArray &)> _onCommandReceived;
|
||||
|
||||
// END Callbacks
|
||||
|
||||
Server(const Server &) = delete;
|
||||
Server &operator=(const Server &) = delete;
|
||||
|
||||
|
|
Loading…
Reference in a new issue