[General] Introduce chat channels

sol2-server-rewrite
Koncord 7 years ago
parent c55f0f73b8
commit aa183e6844

@ -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)
{
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)
{
chatMessage = message;
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"/>
</Widget>
<!-- 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"/>
<!-- 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 type="EditBox" skin="TES3MP_ChatMsg" position="0 338 384 28" align="HStretch Bottom"
name="edit_Command">
<Property key="InvertSelected" value="false"/>
</Widget>
</Widget>
</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…
Cancel
Save