aboutsummaryrefslogtreecommitdiff
path: root/src/modchannels.cpp
blob: 301dcb0923c6bd47a6c454bd030426873c18471a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
Minetest
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "modchannels.h"
#include <algorithm>
#include <cassert>
#include "util/basic_macros.h"

bool ModChannel::registerConsumer(session_t peer_id)
{

	// ignore if peer_id already joined
	if (CONTAINS(m_client_consumers, peer_id))
		return false;

	m_client_consumers.push_back(peer_id);
	return true;
}

bool ModChannel::removeConsumer(session_t peer_id)
{
	bool found = false;
	auto peer_removal_fct = [peer_id, &found](u16 p) {
		if (p == peer_id)
			found = true;

		return p == peer_id;
	};

	m_client_consumers.erase(
			std::remove_if(m_client_consumers.begin(),
					m_client_consumers.end(), peer_removal_fct),
			m_client_consumers.end());

	return found;
}

bool ModChannel::canWrite() const
{
	return m_state == MODCHANNEL_STATE_READ_WRITE;
}

void ModChannel::setState(ModChannelState state)
{
	assert(state != MODCHANNEL_STATE_INIT);

	m_state = state;
}

bool ModChannelMgr::channelRegistered(const std::string &channel) const
{
	return m_registered_channels.find(channel) != m_registered_channels.end();
}

ModChannel *ModChannelMgr::getModChannel(const std::string &channel)
{
	if (!channelRegistered(channel))
		return nullptr;

	return m_registered_channels[channel].get();
}

bool ModChannelMgr::canWriteOnChannel(const std::string &channel) const
{
	const auto channel_it = m_registered_channels.find(channel);
	if (channel_it == m_registered_channels.end()) {
		return false;
	}

	return channel_it->second->canWrite();
}

void ModChannelMgr::registerChannel(const std::string &channel)
{
	m_registered_channels[channel] =
			std::unique_ptr<ModChannel>(new ModChannel(channel));
}

bool ModChannelMgr::setChannelState(const std::string &channel, ModChannelState state)
{
	if (!channelRegistered(channel))
		return false;

	auto channel_it = m_registered_channels.find(channel);
	channel_it->second->setState(state);

	return true;
}

bool ModChannelMgr::removeChannel(const std::string &channel)
{
	if (!channelRegistered(channel))
		return false;

	m_registered_channels.erase(channel);
	return true;
}

bool ModChannelMgr::joinChannel(const std::string &channel, session_t peer_id)
{
	if (!channelRegistered(channel))
		registerChannel(channel);

	return m_registered_channels[channel]->registerConsumer(peer_id);
}

bool ModChannelMgr::leaveChannel(const std::string &channel, session_t peer_id)
{
	if (!channelRegistered(channel))
		return false;

	// Remove consumer from channel
	bool consumerRemoved = m_registered_channels[channel]->removeConsumer(peer_id);

	// If channel is empty, remove it
	if (m_registered_channels[channel]->getChannelPeers().empty()) {
		removeChannel(channel);
	}
	return consumerRemoved;
}

void ModChannelMgr::leaveAllChannels(session_t peer_id)
{
	for (auto &channel_it : m_registered_channels)
		channel_it.second->removeConsumer(peer_id);
}

static std::vector<u16> empty_channel_list;
const std::vector<u16> &ModChannelMgr::getChannelPeers(const std::string &channel) const
{
	const auto &channel_it = m_registered_channels.find(channel);
	if (channel_it == m_registered_channels.end())
		return empty_channel_list;

	return channel_it->second->getChannelPeers();
}
19'>619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include <iostream> 
#include <string>
#include <map>

#include "guiConfigureWorld.h"
#include "guiMessageMenu.h"
#include <IGUIButton.h>
#include <IGUICheckBox.h>
#include <IGUIListBox.h>
#include <IGUIStaticText.h>
#include <IGUITreeView.h>
#include "gettext.h"
#include "util/string.h"
#include "settings.h"
#include "filesys.h"

enum
{
	GUI_ID_MOD_TREEVIEW = 101,
	GUI_ID_ENABLED_CHECKBOX,
	GUI_ID_ENABLEALL,
	GUI_ID_DISABLEALL,
	GUI_ID_DEPENDS_LISTBOX,
	GUI_ID_RDEPENDS_LISTBOX,
	GUI_ID_CANCEL,
	GUI_ID_SAVE
};

#define QUESTIONMARK_STR L"?"
#define CHECKMARK_STR    L"\411"
#define CROSS_STR        L"\403"

GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env,
		gui::IGUIElement* parent, s32 id,
		IMenuManager *menumgr, WorldSpec wspec):
	GUIModalMenu(env, parent, id, menumgr),
	m_wspec(wspec),
	m_gspec(findWorldSubgame(m_wspec.path)),
	m_menumgr(menumgr)
{
	//will be initialized in regenerateGUI()
	m_treeview=NULL;

	// game mods
	m_gamemods = flattenModTree(getModsInPath(m_gspec.gamemods_path));

	// world mods
	std::string worldmods_path = wspec.path + DIR_DELIM + "worldmods";
	m_worldmods = flattenModTree(getModsInPath(worldmods_path));

	// fill m_addontree with add-on mods
	std::set<std::string> paths = m_gspec.addon_mods_paths;
	for(std::set<std::string>::iterator it=paths.begin();
		it != paths.end(); ++it)
	{
		std::map<std::string,ModSpec> mods = getModsInPath(*it);
		m_addontree.insert(mods.begin(), mods.end());
	}

	// expand modpacks
	m_addonmods = flattenModTree(m_addontree);

	// collect reverse dependencies
	for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
		it != m_addonmods.end(); ++it)
	{
		std::string modname = (*it).first;
		ModSpec mod = (*it).second;
		for(std::set<std::string>::iterator dep_it = mod.depends.begin();
			dep_it != mod.depends.end(); ++dep_it)
		{
			m_reverse_depends.insert(std::make_pair((*dep_it),modname));
		}
	}

	m_settings.readConfigFile((m_wspec.path + DIR_DELIM + "world.mt").c_str());
	std::vector<std::string> names = m_settings.getNames();

	// mod_names contains the names of mods mentioned in the world.mt file
	std::set<std::string> mod_names;
	for(std::vector<std::string>::iterator it = names.begin(); 
		it != names.end(); ++it)
	{	
		std::string name = *it;  
		if (name.compare(0,9,"load_mod_")==0)
			mod_names.insert(name.substr(9));
	}

	// find new mods (installed but not mentioned in world.mt)
	for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
		it != m_addonmods.end(); ++it)
	{
		std::string modname = (*it).first;
		ModSpec mod = (*it).second;
		// a mod is new if it is not a modpack, and does not occur in
		// mod_names
		if(!mod.is_modpack &&
		   mod_names.count(modname) == 0)
			m_settings.setBool("load_mod_"+modname, false);
	}
	// find missing mods (mentioned in world.mt, but not installed)
	for(std::set<std::string>::iterator it = mod_names.begin();
		it != mod_names.end(); ++it)
	{
		std::string modname = *it;
		if(m_addonmods.count(modname) == 0)
			m_settings.remove("load_mod_"+modname);
	}
	std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
	m_settings.updateConfigFile(worldmtfile.c_str());
}

void GUIConfigureWorld::drawMenu()
{
	gui::IGUISkin* skin = Environment->getSkin();
	if (!skin)
		return;
	video::IVideoDriver* driver = Environment->getVideoDriver();
	
	video::SColor bgcolor(140,0,0,0);
	driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);

	gui::IGUIElement::draw();
}


void GUIConfigureWorld::regenerateGui(v2u32 screensize)
{

	/*
		Remove stuff
	*/
	removeChildren();
	
	/*
		Calculate new sizes and positions
	*/
	core::rect<s32> rect(
			screensize.X/2 - 580/2,
			screensize.Y/2 - 300/2,
			screensize.X/2 + 580/2,
			screensize.Y/2 + 300/2
	);
	
	DesiredRect = rect;
	recalculateAbsolutePosition(false);

	v2s32 topleft = v2s32(10, 10);

	/*
		Add stuff
	*/
	changeCtype("");
	{
		core::rect<s32> rect(0, 0, 200, 20);
		rect += topleft;
		//proper text is set below, when a mod is selected
		m_modname_text = Environment->addStaticText(L"Mod: N/A", rect, false, 
													false, this, -1);
	}
	{
		core::rect<s32> rect(0, 0, 200, 20);
		rect += v2s32(0, 25) + topleft;
		wchar_t* text = wgettext("enabled");
		m_enabled_checkbox = 
			Environment->addCheckBox(false, rect, this, GUI_ID_ENABLED_CHECKBOX, 
									 text);
		delete[] text;
		m_enabled_checkbox->setVisible(false);
	}
	{
		core::rect<s32> rect(0, 0, 85, 30);
		rect = rect + v2s32(0, 25) + topleft;
		wchar_t* text = wgettext("Enable All");
		m_enableall = Environment->addButton(rect, this, GUI_ID_ENABLEALL,
											 text);
		delete[] text;
		m_enableall->setVisible(false);
	}
	{
		core::rect<s32> rect(0, 0, 85, 30);
		rect = rect + v2s32(115, 25) + topleft;
		wchar_t* text = wgettext("Disable All");
		m_disableall = Environment->addButton(rect, this, GUI_ID_DISABLEALL, text );
		delete[] text;
		m_disableall->setVisible(false);
	}
	{
		core::rect<s32> rect(0, 0, 200, 20);
		rect += v2s32(0, 60) + topleft;
		wchar_t* text = wgettext("depends on:");
		Environment->addStaticText(text, rect, false, false, this, -1);
		delete[] text;
	}
	{
		core::rect<s32> rect(0, 0, 200, 85);
		rect += v2s32(0, 80) + topleft;
		m_dependencies_listbox = 
			Environment->addListBox(rect, this, GUI_ID_DEPENDS_LISTBOX, true);
	}
	{
		core::rect<s32> rect(0, 0, 200, 20);
		rect += v2s32(0, 175) + topleft;
		wchar_t* text = wgettext("is required by:");
		Environment->addStaticText( text, rect, false, false, this, -1);
		delete[] text;
	}
	{
		core::rect<s32> rect(0, 0, 200, 85);
		rect += v2s32(0, 195) + topleft;
		m_rdependencies_listbox = 
			Environment->addListBox(rect,this, GUI_ID_RDEPENDS_LISTBOX,true);
	}
	{
		core::rect<s32> rect(0, 0, 340, 250);
		rect += v2s32(220, 0) + topleft;
		m_treeview = Environment->addTreeView(rect, this,
											  GUI_ID_MOD_TREEVIEW,true);
		gui::IGUITreeViewNode* node 
			= m_treeview->getRoot()->addChildBack(L"Add-Ons");
		buildTreeView(m_addontree, node);
	}
	{
		core::rect<s32> rect(0, 0, 120, 30);
		rect = rect + v2s32(330, 270) - topleft;
		wchar_t* text = wgettext("Cancel");
		Environment->addButton(rect, this, GUI_ID_CANCEL, text);
		delete[] text;
	}
	{
		core::rect<s32> rect(0, 0, 120, 30);
		rect = rect + v2s32(460, 270) - topleft;
		wchar_t* text = wgettext("Save");
		Environment->addButton(rect, this, GUI_ID_SAVE, text);
		delete[] text;
	}
	changeCtype("C");

	// at start, none of the treeview nodes is selected, so we select
	// the first element in the treeview of mods manually here.
	if(m_treeview->getRoot()->hasChilds())
	{
		m_treeview->getRoot()->getFirstChild()->setExpanded(true);
		m_treeview->getRoot()->getFirstChild()->setSelected(true);
		// Because a manual ->setSelected() doesn't cause an event, we
		// have to do this here:
		adjustSidebar();
	}
}

bool GUIConfigureWorld::OnEvent(const SEvent& event)
{

	gui::IGUITreeViewNode* selected_node = NULL;
	if(m_treeview != NULL)
		selected_node = m_treeview->getSelected();

	if(event.EventType==EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
	{
		switch (event.KeyInput.Key) {
		case KEY_ESCAPE: {
			quitMenu();
			return true;
		}
		//	irrlicht's built-in TreeView gui has no keyboard control,
		//	so we do it here: up/down to select prev/next node,
		//	left/right to collapse/expand nodes, space to toggle
		//	enabled/disabled.
		case KEY_DOWN: {
			if(selected_node != NULL)
			{
				gui::IGUITreeViewNode* node = selected_node->getNextVisible();
				if(node != NULL)
				{
					node->setSelected(true);
					adjustSidebar();
				}
			}
			return true;
		}
		case KEY_UP: {
			if(selected_node != NULL)
			{
				gui::IGUITreeViewNode* node = selected_node->getPrevSibling();
				if(node!=NULL)
				{
					node->setSelected(true);
					adjustSidebar();
				}
				else 
				{
					gui::IGUITreeViewNode* parent = selected_node->getParent();
					if(selected_node == parent->getFirstChild() &&
					   parent != m_treeview->getRoot()) 
					{
						parent->setSelected(true);
						adjustSidebar();
					}
				}
			}
			return true;
		}
		case KEY_RIGHT: {
			if(selected_node != NULL && selected_node->hasChilds())
				selected_node->setExpanded(true);
			return true;
		}
		case KEY_LEFT: {
			if(selected_node != NULL && selected_node->hasChilds())
				selected_node->setExpanded(false);
			return true;
		}
		case KEY_SPACE: {
			if(selected_node != NULL && !selected_node->hasChilds() && 
			   selected_node->getText() != NULL)
			{
				std::string modname = wide_to_narrow(selected_node->getText());
				bool checked = m_enabled_checkbox->isChecked();
				m_enabled_checkbox->setChecked(!checked);
				setEnabled(modname,!checked);
			}
			return true;
		}
		default: {}
		}
	}
	if(event.EventType==EET_GUI_EVENT)
	{
		if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
				&& isVisible())
		{
			if(!canTakeFocus(event.GUIEvent.Element))
			{
				dstream<<"GUIConfigureWorld: Not allowing focus change."
						<<std::endl;
				// Returning true disables focus change
				return true;
			}
		}
		if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
			switch(event.GUIEvent.Caller->getID()){
			case GUI_ID_CANCEL: {
				quitMenu();
				return true;
			}
			case GUI_ID_SAVE: {
				std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
				m_settings.updateConfigFile(worldmtfile.c_str());

				// The trailing spaces are because there seems to be a
				// bug in the text-size calculation. if the trailing
				// spaces are removed from the message text, the
				// message gets wrapped and parts of it are cut off:
				wchar_t* text = wgettext("Configuration saved.  ");
				GUIMessageMenu *menu = 
					new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
									 text );
				delete[] text;
				menu->drop();

				try
				{
					ModConfiguration modconf(m_wspec.path);
					if(!modconf.isConsistent())
					{
						wchar_t* text = wgettext("Warning: Configuration not consistent.  ");
						GUIMessageMenu *menu =
							new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
										 text );
						delete[] text;
						menu->drop();
					}
				}
				catch(ModError &err)
				{
					errorstream<<err.what()<<std::endl;
					std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
					GUIMessageMenu *menu =
						new GUIMessageMenu(Environment, Parent, -1, m_menumgr, 
									 text );
					menu->drop();
				}

				quitMenu();	
				return true;
			}
			case GUI_ID_ENABLEALL: {