forked from teamnwah/openmw-tes3coop
[General] Introduce chat channels
This commit is contained in:
parent
c55f0f73b8
commit
aa183e6844
23 changed files with 605 additions and 99 deletions
|
@ -117,10 +117,8 @@ void Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newA
|
|||
|
||||
bool Cell::containsActor(int refNumIndex, int mpNum)
|
||||
{
|
||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||
for (const auto &actor : cellActorList.baseActors)
|
||||
{
|
||||
auto &actor = cellActorList.baseActors.at(i);
|
||||
|
||||
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||
return true;
|
||||
}
|
||||
|
@ -129,14 +127,12 @@ bool Cell::containsActor(int refNumIndex, int mpNum)
|
|||
|
||||
mwmp::BaseActor *Cell::getActor(int refNumIndex, int mpNum)
|
||||
{
|
||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||
for (const auto &actor : cellActorList.baseActors)
|
||||
{
|
||||
auto &actor = cellActorList.baseActors.at(i);
|
||||
|
||||
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||
return actor.get();
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Cell::removeActors(const mwmp::BaseActorList *newActorList)
|
||||
|
|
|
@ -57,6 +57,7 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
|||
serverPassword = TES3MP_DEFAULT_PASSW;
|
||||
|
||||
ProcessorInitializer();
|
||||
createChannel(); // create Default channel
|
||||
}
|
||||
|
||||
Networking::~Networking()
|
||||
|
@ -545,3 +546,41 @@ void Networking::postInit()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ChatChannel> Networking::getChannel(unsigned id)
|
||||
{
|
||||
auto it = chatChannels.find(id);
|
||||
if (it != chatChannels.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned Networking::createChannel()
|
||||
{
|
||||
static unsigned lastChatId = 0;
|
||||
unsigned id = 0;
|
||||
for(auto &channel : chatChannels)
|
||||
{
|
||||
if(channel.second == nullptr)
|
||||
id = channel.first;
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
id = lastChatId++;
|
||||
|
||||
|
||||
chatChannels[id] = make_shared<ChatChannel>();
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Networking::closeChannel(unsigned id)
|
||||
{
|
||||
auto it = chatChannels.find(id);
|
||||
if (it != chatChannels.end())
|
||||
{
|
||||
it->second = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
class MasterClient;
|
||||
namespace mwmp
|
||||
{
|
||||
struct ChatChannel
|
||||
{
|
||||
ChatChannel()
|
||||
{
|
||||
}
|
||||
std::vector<std::weak_ptr<Player>> members;
|
||||
};
|
||||
|
||||
class Networking
|
||||
{
|
||||
public:
|
||||
|
@ -63,6 +71,11 @@ namespace mwmp
|
|||
static Networking *getPtr();
|
||||
|
||||
void postInit();
|
||||
|
||||
std::shared_ptr<ChatChannel> getChannel(unsigned id);
|
||||
unsigned createChannel();
|
||||
bool closeChannel(unsigned id);
|
||||
|
||||
private:
|
||||
LuaState luaState;
|
||||
PacketPreInit::PluginContainer getPluginListSample();
|
||||
|
@ -83,6 +96,8 @@ namespace mwmp
|
|||
bool running;
|
||||
int exitCode;
|
||||
PacketPreInit::PluginContainer samples;
|
||||
|
||||
std::unordered_map<unsigned, std::shared_ptr<ChatChannel>> chatChannels;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "Player.hpp"
|
||||
#include "Inventory.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "Players.hpp"
|
||||
#include "Script/EventController.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -39,7 +41,13 @@ void Player::Init(LuaState &lua)
|
|||
"getAvgPing", &Player::getAvgPing,
|
||||
|
||||
"message", &Player::message,
|
||||
"cleanChat", &Player::cleanChat,
|
||||
"joinToChannel", &Player::joinToChannel,
|
||||
"cleanChannel", &Player::cleanChannel,
|
||||
"renameChannel", &Player::renameChannel,
|
||||
"closeChannel", &Player::closeChannel,
|
||||
"leaveChannel", &Player::leaveChannel,
|
||||
"setChannel", &Player::setChannel,
|
||||
"isChannelOpened", &Player::isChannelOpened,
|
||||
|
||||
"pid", sol::readonly_property(&Player::id),
|
||||
"guid", sol::readonly_property(&Player::getGUID),
|
||||
|
@ -85,6 +93,12 @@ void Player::Init(LuaState &lua)
|
|||
"setAuthority", &Player::setAuthority,
|
||||
"customData", &Player::customData
|
||||
);
|
||||
|
||||
lua.getState()->new_enum("ChannelAction",
|
||||
"createChannel", 0,
|
||||
"joinChannel", 1,
|
||||
"closeChannel", 2,
|
||||
"leftChannel", 3);
|
||||
}
|
||||
|
||||
Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid), NetActor(), changedMap(false), cClass(this),
|
||||
|
@ -310,9 +324,11 @@ void Player::ban() const
|
|||
netCtrl->banAddress(addr.ToString(false));
|
||||
}
|
||||
|
||||
void Player::cleanChat()
|
||||
void Player::cleanChannel(unsigned channelId)
|
||||
{
|
||||
chatMessage.clear();
|
||||
chat.action = mwmp::Chat::Action::clear;
|
||||
chat.channel = channelId;
|
||||
chat.message.clear();
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController();
|
||||
packet->GetPacket(ID_CHAT_MESSAGE)->setPlayer(this);
|
||||
|
@ -352,16 +368,142 @@ void Player::setCharGenStages(int currentStage, int endStage)
|
|||
packet->Send(false);
|
||||
}
|
||||
|
||||
void Player::message(const std::string &message, bool toAll)
|
||||
void Player::message(unsigned channelId, const std::string &message, bool toAll)
|
||||
{
|
||||
chatMessage = message;
|
||||
if (isChannelOpened(channelId))
|
||||
{
|
||||
mwmp::Chat tmp;
|
||||
tmp.action = mwmp::Chat::Action::print;
|
||||
tmp.channel = channelId;
|
||||
tmp.message = message;
|
||||
chat = tmp;
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||
packet->setPlayer(this);
|
||||
|
||||
packet->Send(false);
|
||||
if (toAll)
|
||||
{
|
||||
auto channel = mwmp::Networking::get().getChannel(channelId);
|
||||
|
||||
for (auto it = channel->members.begin(); it != channel->members.end();)
|
||||
{
|
||||
if (auto member = it->lock())
|
||||
{
|
||||
++it;
|
||||
if (member->guid == this->guid)
|
||||
continue;
|
||||
member->chat = tmp;
|
||||
packet->setPlayer(member.get());
|
||||
packet->Send(false);
|
||||
|
||||
}
|
||||
else
|
||||
it = channel->members.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Player::joinToChannel(unsigned channelId, const std::string &name)
|
||||
{
|
||||
auto channel = mwmp::Networking::get().getChannel(channelId);
|
||||
|
||||
if (channel == nullptr) // channel not found
|
||||
return false;
|
||||
|
||||
for (const auto &weakMember : channel->members)
|
||||
{
|
||||
if (auto member = weakMember.lock())
|
||||
{
|
||||
if (member->guid == guid)
|
||||
return false; // player already member of the channel
|
||||
}
|
||||
}
|
||||
auto thisPl = Players::getPlayerByGUID(guid);
|
||||
channel->members.push_back(thisPl);
|
||||
|
||||
chat.action = mwmp::Chat::Action::addchannel;
|
||||
chat.channel = channelId;
|
||||
chat.message = name;
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||
packet->setPlayer(this);
|
||||
packet->Send(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Player::renameChannel(unsigned channelId, const std::string &name)
|
||||
{
|
||||
if (isChannelOpened(channelId))
|
||||
{
|
||||
chat.action = mwmp::Chat::Action::renamechannel;
|
||||
chat.channel = channelId;
|
||||
chat.message = name;
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||
packet->setPlayer(this);
|
||||
|
||||
packet->Send(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::leaveChannel(unsigned channelId)
|
||||
{
|
||||
chat.action = mwmp::Chat::Action::closechannel;
|
||||
chat.channel = channelId;
|
||||
chat.message.clear();
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||
packet->setPlayer(this);
|
||||
|
||||
packet->Send(false);
|
||||
if (toAll)
|
||||
packet->Send(true);
|
||||
|
||||
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CHANNEL_ACTION>(this, channelId, 2); // leaved channel
|
||||
}
|
||||
|
||||
void Player::closeChannel(unsigned channelId)
|
||||
{
|
||||
auto channel = mwmp::Networking::get().getChannel(channelId);
|
||||
|
||||
for(auto &weakMember : channel->members) // kick members from channel before deleting channel
|
||||
{
|
||||
if(auto member = weakMember.lock())
|
||||
member->leaveChannel(channelId);
|
||||
}
|
||||
|
||||
if (!mwmp::Networking::get().closeChannel(channelId)) // cannot close channel
|
||||
return;
|
||||
|
||||
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CHANNEL_ACTION>(this, channelId, 2); // channel closed
|
||||
}
|
||||
|
||||
void Player::setChannel(unsigned channelId)
|
||||
{
|
||||
if (isChannelOpened(channelId))
|
||||
{
|
||||
chat.action = mwmp::Chat::Action::setchannel;
|
||||
chat.channel = channelId;
|
||||
chat.message.clear();
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||
packet->setPlayer(this);
|
||||
|
||||
packet->Send(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Player::isChannelOpened(unsigned channelId)
|
||||
{
|
||||
auto channel = mwmp::Networking::get().getChannel(channelId);
|
||||
|
||||
auto it = std::find_if(channel->members.begin(), channel->members.end(), [this](const auto &weakMember){
|
||||
if(auto member = weakMember.lock())
|
||||
return member->guid == guid;
|
||||
return false;
|
||||
});
|
||||
return it != channel->members.end();
|
||||
}
|
||||
|
||||
int Player::getLevel() const
|
||||
|
|
|
@ -75,14 +75,21 @@ public:
|
|||
public:
|
||||
void kick() const;
|
||||
void ban() const;
|
||||
void cleanChat();
|
||||
|
||||
void cleanChannel(unsigned channelId);
|
||||
void message(unsigned channelId, const std::string &message, bool toAll = false);
|
||||
bool joinToChannel(unsigned channelId, const std::string &name);
|
||||
void renameChannel(unsigned channelId, const std::string &name);
|
||||
void closeChannel(unsigned channelId);
|
||||
void leaveChannel(unsigned channelId);
|
||||
void setChannel(unsigned channelId);
|
||||
bool isChannelOpened(unsigned channelId);
|
||||
|
||||
int getAvgPing();
|
||||
|
||||
std::string getName();
|
||||
void setName(const std::string &name);
|
||||
void setCharGenStages(int currentStage, int endStage);
|
||||
void message(const std::string &message, bool toAll = false);
|
||||
int getGender() const;
|
||||
void setGender(int gender);
|
||||
std::string getRace() const;
|
||||
|
|
|
@ -38,17 +38,26 @@ std::shared_ptr<Player> Players::getPlayerByGUID(RakNet::RakNetGUID guid)
|
|||
const auto &ls = store.get<ByGUID>();
|
||||
auto it = ls.find(guid.g);
|
||||
if (it != ls.end())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_TRACE, "%d references: %d", guid.g, it->use_count());
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void deleter(Player *pl)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Player %lu deleted", pl->guid.g);
|
||||
delete pl;
|
||||
}
|
||||
|
||||
std::shared_ptr<Player> Players::addPlayer(RakNet::RakNetGUID guid)
|
||||
{
|
||||
const int maxConnections = 65535;
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Creating new player with guid %lu", guid.g);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Creating new player with guid %lu", guid.g);
|
||||
|
||||
auto player = make_shared<Player>(guid);
|
||||
auto player = shared_ptr<Player>(new Player(guid), deleter);
|
||||
|
||||
unsigned short findPid = 0;
|
||||
const auto &ls = store.get<ByID>();
|
||||
|
@ -71,25 +80,27 @@ std::shared_ptr<Player> Players::addPlayer(RakNet::RakNetGUID guid)
|
|||
|
||||
void Players::deletePlayerByPID(int pid)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Marking player (pid %i) for deletion", pid);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (pid %i) for deletion", pid);
|
||||
auto &ls = store.get<ByID>();
|
||||
auto it = ls.find(pid);
|
||||
if (it != ls.end())
|
||||
{
|
||||
(*it)->markedForDeletion = true;
|
||||
ls.erase(it);
|
||||
LOG_APPEND(Log::LOG_TRACE, "- references: %d", it->use_count());
|
||||
}
|
||||
}
|
||||
|
||||
void Players::deletePlayerByGUID(RakNet::RakNetGUID guid)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Marking player (guid %lu) for deletion", guid.g);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (guid %lu) for deletion", guid.g);
|
||||
auto &ls = store.get<ByGUID>();
|
||||
auto it = ls.find(guid.g);
|
||||
if (it != ls.end())
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- references: %d", it->use_count());
|
||||
(*it)->markedForDeletion = true;
|
||||
ls.erase(it);
|
||||
LOG_APPEND(Log::LOG_INFO, "- references: %d", it->use_count());
|
||||
LOG_APPEND(Log::LOG_TRACE, "- references: %d", it->use_count());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,8 @@ bool CommandController::hasCommand(const std::string &command)
|
|||
return commands.find(command) != commands.end();
|
||||
}
|
||||
|
||||
std::pair<CommandController::ExecResult, std::string> CommandController::exec(std::shared_ptr<Player> player, const std::string &message)
|
||||
std::pair<CommandController::ExecResult, std::string> CommandController::exec(const std::shared_ptr<Player> &player,
|
||||
const std::string &message, unsigned channel)
|
||||
{
|
||||
char cmdChar = message[0];
|
||||
if (message.size() < 2 || (cmdChar != '/' && cmdChar != '!'))
|
||||
|
@ -72,7 +73,7 @@ std::pair<CommandController::ExecResult, std::string> CommandController::exec(st
|
|||
if (cmd != commands.end())
|
||||
{
|
||||
tokens.pop_front();
|
||||
bool result = cmd->second.func(player, sol::as_table(tokens));
|
||||
bool result = cmd->second.func(player, sol::as_table(tokens), channel);
|
||||
if (result)
|
||||
return make_pair(ExecResult::SUCCESS, "");
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
*/
|
||||
bool hasCommand(const std::string &command);
|
||||
|
||||
std::pair<ExecResult, std::string> exec(std::shared_ptr<Player> player, const std::string &message);
|
||||
std::pair<ExecResult, std::string> exec(const std::shared_ptr<Player> &player, const std::string &message, unsigned channel);
|
||||
private:
|
||||
std::deque<std::string> cmdParser(const std::string &message);
|
||||
Container commands;
|
||||
|
|
|
@ -64,6 +64,7 @@ EventController::EventController(LuaState *luaCtrl)
|
|||
ADD_CORE_EVENT(ON_PLAYER_REST),
|
||||
ADD_CORE_EVENT(ON_PLAYER_SENDMESSAGE),
|
||||
ADD_CORE_EVENT(ON_PLAYER_ENDCHARGEN),
|
||||
ADD_CORE_EVENT(ON_CHANNEL_ACTION),
|
||||
ADD_CORE_EVENT(ON_GUI_ACTION),
|
||||
ADD_CORE_EVENT(ON_REQUEST_PLUGIN_LIST),
|
||||
ADD_CORE_EVENT(ON_MP_REFNUM),
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace CoreEvent
|
|||
ON_PLAYER_SENDMESSAGE,
|
||||
ON_PLAYER_ENDCHARGEN,
|
||||
|
||||
ON_CHANNEL_ACTION,
|
||||
|
||||
ON_GUI_ACTION,
|
||||
ON_MP_REFNUM,
|
||||
|
||||
|
|
|
@ -279,6 +279,10 @@ LuaState::LuaState()
|
|||
packet->Send(false);
|
||||
});
|
||||
});
|
||||
|
||||
lua->set_function("createChannel", [](){
|
||||
return mwmp::Networking::get().createChannel();
|
||||
});
|
||||
}
|
||||
|
||||
sol::environment LuaState::openModule(std::string homePath, std::string moduleName)
|
||||
|
|
|
@ -23,19 +23,19 @@ namespace mwmp
|
|||
|
||||
auto &lua = Networking::get().getState();
|
||||
|
||||
auto cmdExecResult = lua.getCmdCtrl().exec(player, player->chatMessage);
|
||||
auto cmdExecResult = lua.getCmdCtrl().exec(player, player->chat.message, player->chat.channel);
|
||||
switch (cmdExecResult.first)
|
||||
{
|
||||
case CommandController::ExecResult::NOT_FOUND: // err cmd not found
|
||||
player->message("#FF0000Command not found.\n"); // inform player that command not found
|
||||
player->message(player->chat.channel, "#FF0000Command not found.\n"); // inform player that command not found
|
||||
break;
|
||||
case CommandController::ExecResult::NOT_CMD: // cmd length < 2 or message is not cmd
|
||||
lua.getEventCtrl().Call<CoreEvent::ON_PLAYER_SENDMESSAGE>(player, player->chatMessage);
|
||||
lua.getEventCtrl().Call<CoreEvent::ON_PLAYER_SENDMESSAGE>(player, player->chat.message, player->chat.channel);
|
||||
break;
|
||||
case CommandController::ExecResult::SUCCESS: // returned true from function
|
||||
break;
|
||||
case CommandController::ExecResult::FAIL: // returned false from function
|
||||
player->message("#B8860B"+cmdExecResult.second); // show "help msg" to player
|
||||
player->message(player->chat.channel, "#B8860B"+cmdExecResult.second); // show "help msg" to player
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ namespace MWMechanics
|
|||
|
||||
namespace mwmp
|
||||
{
|
||||
struct DedicatedPlayer;
|
||||
|
||||
class DedicatedPlayer : public BasePlayer
|
||||
{
|
||||
friend class PlayerList;
|
||||
|
|
|
@ -10,11 +10,17 @@
|
|||
#include "apps/openmw/mwinput/inputmanagerimp.hpp"
|
||||
#include <MyGUI_InputManager.h>
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "../Networking.hpp"
|
||||
#include "../Main.hpp"
|
||||
#include "../LocalPlayer.hpp"
|
||||
|
||||
#include "../../mwbase/world.hpp"
|
||||
|
||||
#include "../../mwworld/timestamp.hpp"
|
||||
|
||||
#include "../GUIController.hpp"
|
||||
|
||||
|
||||
|
@ -28,6 +34,17 @@ namespace mwmp
|
|||
|
||||
getWidget(mCommandLine, "edit_Command");
|
||||
getWidget(mHistory, "list_History");
|
||||
getWidget(mChannelPrevBtn, "btn_prev_ch");
|
||||
mChannelPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::prevChannels);
|
||||
getWidget(mChannelNextBtn, "btn_next_ch");
|
||||
mChannelNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::nextChannels);
|
||||
getWidget(mBoxChannels, "box_channels");
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
getWidget(mChannelBtns[i], "btn_ch" + std::to_string(i + 1));
|
||||
mChannelBtns[i]->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::onClickChannel);
|
||||
}
|
||||
|
||||
// Set up the command line box
|
||||
mCommandLine->eventEditSelectAccept +=
|
||||
|
@ -35,8 +52,6 @@ namespace mwmp
|
|||
mCommandLine->eventKeyButtonPressed +=
|
||||
newDelegate(this, &GUIChat::keyPress);
|
||||
|
||||
setTitle("Chat");
|
||||
|
||||
mHistory->setOverflowToTheLeft(true);
|
||||
mHistory->setEditWordWrap(true);
|
||||
mHistory->setTextShadow(true);
|
||||
|
@ -45,8 +60,14 @@ namespace mwmp
|
|||
mHistory->setNeedKeyFocus(false);
|
||||
|
||||
windowState = 0;
|
||||
mCommandLine->setVisible(0);
|
||||
mCommandLine->setVisible(false);
|
||||
mBoxChannels->setVisible(false);
|
||||
delay = 3; // 3 sec.
|
||||
page = 0;
|
||||
currentChannel = 0;
|
||||
addChannel(0, "Default");
|
||||
setChannel(0);
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::onOpen()
|
||||
|
@ -73,18 +94,42 @@ namespace mwmp
|
|||
|
||||
void GUIChat::acceptCommand(MyGUI::EditBox *_sender)
|
||||
{
|
||||
const std::string &cm = MyGUI::TextIterator::toTagsString(mCommandLine->getCaption());
|
||||
const std::string &cm = MyGUI::TextIterator::toTagsString(mCommandLine->getCaption());
|
||||
|
||||
// If they enter nothing, then it should be canceled.
|
||||
// Otherwise, there's no way of closing without having text.
|
||||
if (cm.empty())
|
||||
{
|
||||
mCommandLine->setCaption("");
|
||||
SetEditState(0);
|
||||
SetEditState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Player: %s", cm.c_str());
|
||||
if (cm.find("//", 0, 2) == 0) //clientside commands
|
||||
{
|
||||
typedef boost::char_separator<char> csep;
|
||||
csep sep(" ");
|
||||
boost::tokenizer<csep> tokens(cm, sep);
|
||||
|
||||
std::vector<std::string> cmd;
|
||||
for (const auto& t : tokens)
|
||||
cmd.push_back(t);
|
||||
|
||||
if (cmd[0] == "//channel")
|
||||
{
|
||||
if (cmd[1] == "close")
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Closed \"%d\" channel", currentChannel);
|
||||
closeChannel(currentChannel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Player: %s", cm);
|
||||
send (cm);
|
||||
}
|
||||
|
||||
// Add the command to the history, and set the current pointer to
|
||||
// the end of the list
|
||||
|
@ -93,12 +138,9 @@ namespace mwmp
|
|||
mCurrent = mCommandHistory.end();
|
||||
mEditString.clear();
|
||||
|
||||
// Reset the command line before the command execution.
|
||||
// It prevents the re-triggering of the acceptCommand() event for the same command
|
||||
// during the actual command execution
|
||||
mCommandLine->setCaption("");
|
||||
SetEditState(0);
|
||||
send (cm);
|
||||
SetEditState(false);
|
||||
|
||||
}
|
||||
|
||||
void GUIChat::onResChange(int width, int height)
|
||||
|
@ -112,33 +154,28 @@ namespace mwmp
|
|||
mCommandLine->setFontName(fntName);
|
||||
}
|
||||
|
||||
void GUIChat::print(const std::string &msg, const std::string &color)
|
||||
void GUIChat::print(unsigned channelId, const std::string &msg, const std::string &color)
|
||||
{
|
||||
if (windowState == 2 && !isVisible())
|
||||
{
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
if(msg.size() == 0)
|
||||
if (channelId != currentChannel)
|
||||
{
|
||||
clean();
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Chat cleaned");
|
||||
try
|
||||
{
|
||||
auto it = getChannel(channelId);
|
||||
if (it != channels.end())
|
||||
it->channelText = color + msg;
|
||||
}
|
||||
catch(std::out_of_range &e) {}
|
||||
}
|
||||
else
|
||||
{
|
||||
mHistory->addText(color + msg);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", msg.c_str());
|
||||
}
|
||||
}
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", msg.c_str());
|
||||
|
||||
void GUIChat::printOK(const std::string &msg)
|
||||
{
|
||||
print(msg + "\n", "#FF00FF");
|
||||
}
|
||||
|
||||
void GUIChat::printError(const std::string &msg)
|
||||
{
|
||||
print(msg + "\n", "#FF2222");
|
||||
}
|
||||
|
||||
void GUIChat::send(const std::string &str)
|
||||
|
@ -147,15 +184,26 @@ namespace mwmp
|
|||
|
||||
Networking *networking = Main::get().getNetworking();
|
||||
|
||||
localPlayer->chatMessage = str;
|
||||
localPlayer->chat.action = Chat::Action::print;
|
||||
localPlayer->chat.channel = currentChannel;
|
||||
localPlayer->chat.message = str;
|
||||
|
||||
networking->getPlayerPacket(ID_CHAT_MESSAGE)->setPlayer(localPlayer);
|
||||
networking->getPlayerPacket(ID_CHAT_MESSAGE)->Send();
|
||||
}
|
||||
|
||||
void GUIChat::clean()
|
||||
void GUIChat::clean(unsigned channelId)
|
||||
{
|
||||
mHistory->setCaption("");
|
||||
if(channelId == currentChannel)
|
||||
mHistory->setCaption("");
|
||||
else
|
||||
{
|
||||
auto it = getChannel(channelId);
|
||||
if(it != channels.end())
|
||||
{
|
||||
it->channelText.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIChat::pressedChatMode()
|
||||
|
@ -174,7 +222,7 @@ namespace mwmp
|
|||
{
|
||||
case CHAT_DISABLED:
|
||||
this->mMainWidget->setVisible(false);
|
||||
SetEditState(0);
|
||||
SetEditState(false);
|
||||
break;
|
||||
case CHAT_ENABLED:
|
||||
this->mMainWidget->setVisible(true);
|
||||
|
@ -189,6 +237,7 @@ namespace mwmp
|
|||
{
|
||||
editState = state;
|
||||
mCommandLine->setVisible(editState);
|
||||
mBoxChannels->setVisible(editState);
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(editState ? mCommandLine : nullptr);
|
||||
}
|
||||
|
||||
|
@ -239,7 +288,6 @@ namespace mwmp
|
|||
mCommandLine->setCaption(mEditString);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GUIChat::Update(float dt)
|
||||
|
@ -254,13 +302,18 @@ namespace mwmp
|
|||
}
|
||||
}
|
||||
|
||||
if(netStat)
|
||||
if (netStat)
|
||||
{
|
||||
auto rss = Main::get().getNetworking()->getNetworkStatistics();
|
||||
mHistory->setCaption(rss);
|
||||
}
|
||||
}
|
||||
|
||||
void GUIChat::setCaption(const std::string &str)
|
||||
{
|
||||
mHistory->setCaption(str);
|
||||
}
|
||||
|
||||
void GUIChat::setDelay(float delay)
|
||||
{
|
||||
this->delay = delay;
|
||||
|
@ -270,4 +323,139 @@ namespace mwmp
|
|||
{
|
||||
netStat = !netStat;
|
||||
}
|
||||
|
||||
void GUIChat::addChannel(unsigned ch, const std::string &name)
|
||||
{
|
||||
auto channel = getChannel(ch);
|
||||
if(channel == channels.end())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Adding channel id: %d %s", ch, name);
|
||||
channels.push_back(ChannelData{ch, name.substr(0, 9), ""});
|
||||
}
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::renameChannel(unsigned ch, const std::string &newName)
|
||||
{
|
||||
auto it = getChannel(ch);
|
||||
if(it != channels.end())
|
||||
{
|
||||
it->channelName = newName.substr(0, 9);
|
||||
redrawChnnels();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIChat::setChannel(const std::string &channel, bool saveHistory)
|
||||
{
|
||||
auto ch = channel.substr(0, 9);
|
||||
auto it = std::find_if(channels.begin(), channels.end(), [&ch](const ChannelData &item) {
|
||||
return item.channelName == ch;
|
||||
});
|
||||
|
||||
if (it != channels.end())
|
||||
setChannel(it, saveHistory);
|
||||
}
|
||||
|
||||
void GUIChat::setChannel(unsigned ch, bool saveHistory)
|
||||
{
|
||||
auto it = getChannel(ch);
|
||||
if (it != channels.end())
|
||||
setChannel(it, saveHistory);
|
||||
}
|
||||
|
||||
void GUIChat::setChannel(ChannelIter it, bool saveHistory)
|
||||
{
|
||||
if (saveHistory)
|
||||
channels[currentChannel].channelText = mHistory->getCaption();
|
||||
|
||||
mHistory->setCaption(it->channelText);
|
||||
currentChannel = it->channel;
|
||||
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::closeChannel(unsigned ch)
|
||||
{
|
||||
if (ch == 0) // that's impossible
|
||||
return;
|
||||
auto it = getChannel(ch);
|
||||
|
||||
if (it != channels.end())
|
||||
{
|
||||
if (ch == it->channel)
|
||||
setChannel(0, false); // reset to default channel
|
||||
channels.erase(it);
|
||||
}
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::redrawChnnels()
|
||||
{
|
||||
mChannelPrevBtn->setVisible(page != 0);
|
||||
mChannelNextBtn->setVisible(channels.size() > 3 && page != lastPage());
|
||||
|
||||
if (page >=lastPage())
|
||||
page = lastPage();
|
||||
unsigned showElems = page * pageM + 3;
|
||||
if (showElems > channels.size())
|
||||
showElems = static_cast<unsigned>(channels.size());
|
||||
auto it = channels.begin() + page * pageM;
|
||||
auto endIt = channels.begin() + showElems;
|
||||
for (auto &btn : mChannelBtns)
|
||||
{
|
||||
static const auto defaultColour = btn->getTextColour();
|
||||
if (it == endIt)
|
||||
btn->setVisible(false);
|
||||
else
|
||||
{
|
||||
btn->setVisible(true);
|
||||
if (currentChannel == it->channel)
|
||||
{
|
||||
setTitle("Chat: " + it->channelName + "(" + std::to_string(it->channel) + ")");
|
||||
btn->setEnabled(false);
|
||||
btn->setStateSelected(true);
|
||||
btn->setTextColour(MyGUI::Colour::Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
btn->setEnabled(true);
|
||||
btn->setTextColour(defaultColour);
|
||||
}
|
||||
btn->setCaption(it++->channelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUIChat::nextChannels(MyGUI::Widget* _sender)
|
||||
{
|
||||
page++;
|
||||
if (page > lastPage())
|
||||
page = lastPage();
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::prevChannels(MyGUI::Widget* _sender)
|
||||
{
|
||||
if (page > 0)
|
||||
page--;
|
||||
redrawChnnels();
|
||||
}
|
||||
|
||||
void GUIChat::onClickChannel(MyGUI::Widget *_sender)
|
||||
{
|
||||
auto sender = dynamic_cast<MyGUI::Button*>(_sender);
|
||||
setChannel(sender->getCaption().asUTF8());
|
||||
}
|
||||
|
||||
unsigned GUIChat::lastPage()
|
||||
{
|
||||
return static_cast<unsigned>(channels.size() / pageM + channels.size() % pageM - 1);
|
||||
}
|
||||
|
||||
GUIChat::ChannelIter GUIChat::getChannel(unsigned ch)
|
||||
{
|
||||
return std::find_if(channels.begin(), channels.end(), [&ch](const ChannelData &data){
|
||||
return data.channel == ch;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <components/widgets/box.hpp>
|
||||
|
||||
#include "apps/openmw/mwgui/windowbase.hpp"
|
||||
|
||||
|
@ -27,6 +29,10 @@ namespace mwmp
|
|||
|
||||
MyGUI::EditBox* mCommandLine;
|
||||
MyGUI::EditBox* mHistory;
|
||||
MyGUI::Button* mChannelBtns[3];
|
||||
MyGUI::Button* mChannelNextBtn;
|
||||
MyGUI::Button* mChannelPrevBtn;
|
||||
Gui::HBox* mBoxChannels;
|
||||
|
||||
typedef std::list<std::string> StringList;
|
||||
|
||||
|
@ -43,33 +49,40 @@ namespace mwmp
|
|||
|
||||
void Update(float dt);
|
||||
|
||||
void setCaption(const std::string &str);
|
||||
|
||||
virtual void onOpen();
|
||||
virtual void onClose();
|
||||
|
||||
virtual bool exit();
|
||||
/*virtual void open();
|
||||
virtual void close();*/
|
||||
|
||||
void setFont(const std::string &fntName);
|
||||
|
||||
void onResChange(int width, int height);
|
||||
|
||||
// Print a message to the console, in specified color.
|
||||
void print(const std::string &msg, const std::string& color = "#FFFFFF");
|
||||
void print(unsigned channelId, const std::string &msg, const std::string& color = "#FFFFFF");
|
||||
|
||||
// Clean chat
|
||||
void clean();
|
||||
|
||||
// These are pre-colored versions that you should use.
|
||||
|
||||
/// Output from successful console command
|
||||
void printOK(const std::string &msg);
|
||||
|
||||
/// Error message
|
||||
void printError(const std::string &msg);
|
||||
void clean(unsigned channelId);
|
||||
|
||||
void send(const std::string &str);
|
||||
|
||||
void switchNetstat();
|
||||
|
||||
void addChannel(unsigned ch, const std::string &name);
|
||||
void setChannel(const std::string &newName, bool saveHistory = true);
|
||||
void renameChannel(unsigned ch, const std::string &channel);
|
||||
void setChannel(unsigned ch, bool saveHistory = true);
|
||||
void closeChannel(unsigned ch);
|
||||
void redrawChnnels();
|
||||
unsigned lastPage();
|
||||
void nextChannels(MyGUI::Widget* _sender);
|
||||
void prevChannels(MyGUI::Widget* _sender);
|
||||
void onClickChannel(MyGUI::Widget* _sender);
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
|
@ -87,6 +100,20 @@ namespace mwmp
|
|||
bool netStat;
|
||||
float delay;
|
||||
float curTime;
|
||||
struct ChannelData
|
||||
{
|
||||
unsigned channel;
|
||||
std::string channelName;
|
||||
MyGUI::UString channelText;
|
||||
};
|
||||
std::vector<ChannelData> channels;
|
||||
unsigned currentChannel;
|
||||
unsigned page;
|
||||
const int pageM = 3;
|
||||
|
||||
typedef std::vector<ChannelData>::iterator ChannelIter;
|
||||
void setChannel(ChannelIter iter, bool saveHistory);
|
||||
ChannelIter getChannel(unsigned ch);
|
||||
};
|
||||
}
|
||||
#endif //OPENMW_GUICHAT_HPP
|
||||
|
|
|
@ -77,10 +77,38 @@ void mwmp::GUIController::setupChat(const Settings::Manager &mgr)
|
|||
mChat->setDelay(chatDelay);
|
||||
}
|
||||
|
||||
void mwmp::GUIController::printChatMessage(std::string &msg)
|
||||
void mwmp::GUIController::printChatMessage(const mwmp::Chat &chat)
|
||||
{
|
||||
if (mChat != nullptr)
|
||||
mChat->print(msg);
|
||||
{
|
||||
switch (chat.action)
|
||||
{
|
||||
case Chat::Action::print:
|
||||
mChat->print(chat.channel, chat.message);
|
||||
break;
|
||||
case Chat::Action::clear:
|
||||
mChat->clean(chat.channel);
|
||||
break;
|
||||
case Chat::Action::addchannel:
|
||||
mChat->addChannel(chat.channel, chat.message);
|
||||
break;
|
||||
case Chat::Action::setchannel:
|
||||
mChat->setChannel(chat.channel);
|
||||
break;
|
||||
case Chat::Action::closechannel:
|
||||
mChat->closeChannel(chat.channel);
|
||||
break;
|
||||
case Chat::Action::renamechannel:
|
||||
mChat->renameChannel(chat.channel, chat.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mwmp::GUIController::setChatCaption(const std::string &msg)
|
||||
{
|
||||
if (mChat != nullptr)
|
||||
mChat->setCaption(msg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@ namespace mwmp
|
|||
void cleanUp();
|
||||
void setupChat(const Settings::Manager &manager);
|
||||
|
||||
void printChatMessage(std::string &msg);
|
||||
void printChatMessage(const mwmp::Chat &chat);
|
||||
void setChatCaption(const std::string &msg);
|
||||
void setChatVisible(bool chatVisible);
|
||||
|
||||
void showMessageBox(const BasePlayer::GUIMessageBox &guiMessageBox);
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace MWMechanics
|
|||
|
||||
namespace mwmp
|
||||
{
|
||||
struct DedicatedPlayer;
|
||||
class DedicatedPlayer;
|
||||
|
||||
class PlayerList
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace mwmp
|
|||
virtual void Do(PlayerPacket &packet, BasePlayer *player)
|
||||
{
|
||||
if (player != 0)
|
||||
Main::get().getGUIController()->printChatMessage(player->chatMessage);
|
||||
Main::get().getGUIController()->printChatMessage(player->chat);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,6 +19,21 @@
|
|||
|
||||
namespace mwmp
|
||||
{
|
||||
struct Chat
|
||||
{
|
||||
enum class Action : uint8_t {
|
||||
print = 0,
|
||||
clear,
|
||||
addchannel,
|
||||
setchannel,
|
||||
closechannel,
|
||||
renamechannel
|
||||
};
|
||||
unsigned channel;
|
||||
Action action;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
struct CurrentContainer
|
||||
{
|
||||
std::string refId;
|
||||
|
@ -254,7 +269,7 @@ namespace mwmp
|
|||
ESM::NpcStats npcStats;
|
||||
ESM::Class charClass;
|
||||
std::string birthsign;
|
||||
std::string chatMessage;
|
||||
Chat chat;
|
||||
CharGenState charGenState;
|
||||
std::string passw;
|
||||
|
||||
|
|
|
@ -15,5 +15,7 @@ void mwmp::PacketChatMessage::Packet(RakNet::BitStream *bs, bool send)
|
|||
{
|
||||
PlayerPacket::Packet(bs, send);
|
||||
|
||||
RW(player->chatMessage, send);
|
||||
RW(player->chat.channel, send);
|
||||
RW(player->chat.action, send);
|
||||
RW(player->chat.message, send);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Window" position="0 0 400 400" layer="Console" name="_Main">
|
||||
<Property key="Caption" value="#{sConsoleTitle}"/>
|
||||
<Property key="MinSize" value="40 40"/>
|
||||
<Property key="Visible" value="false"/>
|
||||
<!--<Widget type="Window" skin="TES3MP_ChatWindowBL" position="0 0 400 400" layer="Console" name="_Main">-->
|
||||
<Widget type="Window" skin="MW_Window" position="0 0 400 428" layer="Windows" name="_Main">
|
||||
<Property key="Caption" value="#{sConsoleTitle}"/>
|
||||
<Property key="MinSize" value="40 40"/>
|
||||
<Property key="Visible" value="false"/>
|
||||
|
||||
<!-- Log window -->
|
||||
<Widget type="EditBox" skin="MW_TextBoxEdit" position="5 5 380 328" align="Stretch" name="list_History">
|
||||
<Property key="MultiLine" value="1"/>
|
||||
<Property key="ReadOnly" value="true"/>
|
||||
<Property key="FontName" value="Russo"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<Property key="TextColour" value="1 1 1"/>
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
<!-- Log window -->
|
||||
<Widget type="EditBox" skin="MW_TextBoxEdit" position="5 5 380 328" align="Stretch" name="list_History">
|
||||
<Property key="MultiLine" value="1"/>
|
||||
<Property key="ReadOnly" value="true"/>
|
||||
<Property key="FontName" value="Russo"/>
|
||||
<Property key="TextAlign" value="Left Top"/>
|
||||
<Property key="TextColour" value="1 1 1"/>
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
</Widget>
|
||||
|
||||
|
||||
<!-- Command line -->
|
||||
<Widget type="VBox" align="Bottom HStretch" position="0 338 384 56">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
<Widget type="HBox" align="Bottom HStretch" name="box_channels">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="btn_prev_ch">
|
||||
<Property key="Caption" value="<"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="btn_ch1">
|
||||
<Property key="Caption" value="ZZZZZZZZZ"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="btn_ch2">
|
||||
<Property key="Caption" value="ZZZZZZZZZ"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="btn_ch3">
|
||||
<Property key="Caption" value="ZZZZZZZZZ"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="btn_next_ch">
|
||||
<Property key="Caption" value=">"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="Spacer"/>
|
||||
|
||||
<Widget type="EditBox" skin="TES3MP_ChatMsg" position="0 338 384 28" align="HStretch Bottom"
|
||||
name="edit_Command">
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Command line -->
|
||||
<Widget type="EditBox" skin="TES3MP_ChatMsg" position="0 338 384 28" align="HStretch Bottom" name="edit_Command">
|
||||
<Property key="InvertSelected" value="false"/>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
|
|
@ -17,4 +17,8 @@
|
|||
<Child type="TextBox" skin="TES3MP_EditClient" offset="4 2 19 22" align="Bottom Stretch" name="Client"/>
|
||||
</Resource>
|
||||
|
||||
<Resource type="ResourceSkin" name="TES3MP_ChatWindowBL" size="256 54">
|
||||
<Child type="Widget" skin="BlackBG" offset="0 0 256 256" align="Stretch" name="Client"/>
|
||||
</Resource>
|
||||
|
||||
</MyGUI>
|
||||
|
|
Loading…
Reference in a new issue