Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2013-05-13 18:19:51 +02:00
commit e0564d5de0
37 changed files with 1143 additions and 479 deletions

@ -143,6 +143,7 @@ if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})
install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/")
install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game")
endif()
set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build")
if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE})

@ -30,13 +30,17 @@ This game is not finished
Default Controls
-----------------
- WASD: Move
- Space: Jump
- E: Go down
- Shift: Sneak
- Q: Drop item
- I: Open inventory
- Mouse: Turn/look
- WASD: move
- Space: jump/climb
- Shift: sneak/go down
- Q: drop item
- I: inventory
- Mouse: turn/look
- Mouse left: dig/punch
- Mouse right: place/use
- Mouse wheel: select item
- Esc: pause menu
- T: chat
- Settable in the configuration file, see the section below.
Paths
@ -277,7 +281,7 @@ the Free Software Foundation; either version 2.1 of the License, or
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.
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.,

@ -1,11 +1,11 @@
-- Minetest: builtin/features.lua
minetest.features = {
"glasslike_framed" = true,
"nodebox_as_selectionbox" = true,
"chat_send_player_param3" = true,
"get_all_craft_recipes_works" = true,
"use_texture_alpha" = true,
glasslike_framed = true,
nodebox_as_selectionbox = true,
chat_send_player_param3 = true,
get_all_craft_recipes_works = true,
use_texture_alpha = true,
}
function minetest.has_feature(arg)

@ -120,6 +120,17 @@ depends.txt:
List of mods that have to be loaded before loading this mod.
A single line contains a single modname.
Optional dependencies can be defined by appending a question mark
to a single modname. Their meaning is that if the specified mod
is missing, that does not prevent this mod from being loaded.
optdepends.txt:
An alternative way of specifying optional dependencies.
Like depends.txt, a single line contains a single modname.
NOTE: This file exists for compatibility purposes only and
support for it will be removed from the engine by the end of 2013.
init.lua:
The main Lua script. Running this script should register everything it
wants to register. Subsequent execution depends on minetest calling the

@ -61,6 +61,9 @@ Run dedicated server
\-\-speedtests
Run speed tests
.TP
\-\-videomodes
List available video modes
.TP
\-\-info
Print more information to console
.TP

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
games/minimal/menu/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

@ -151,6 +151,8 @@
#crosshair_color = (255,255,255)
# Cross alpha (opaqueness, between 0 and 255)
#crosshair_alpha = 255
# Sensitivity multiplier
#mouse_sensitivity = 0.2
# Sound settings
#enable_sound = true
#sound_volume = 0.7

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock.h"
#include "settings.h"
#include "profiler.h"
#include "gettext.h"
#include "log.h"
#include "nodemetadata.h"
#include "nodedef.h"
@ -2555,6 +2556,9 @@ void Client::inventoryAction(InventoryAction *a)
Predict some local inventory changes
*/
a->clientApply(this, this);
// Remove it
delete a;
}
ClientActiveObject * Client::getSelectedActiveObject(
@ -2801,7 +2805,10 @@ ClientEvent Client::getClientEvent()
return m_client_event_queue.pop_front();
}
void Client::afterContentReceived()
void draw_load_screen(const std::wstring &text,
IrrlichtDevice* device, gui::IGUIFont* font,
float dtime=0 ,int percent=0, bool clouds=true);
void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
{
infostream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received);
@ -2836,13 +2843,23 @@ void Client::afterContentReceived()
if(g_settings->getBool("preload_item_visuals"))
{
verbosestream<<"Updating item textures and meshes"<<std::endl;
wchar_t* text = wgettext("Item textures...");
draw_load_screen(text,device,font,0,0);
std::set<std::string> names = m_itemdef->getAll();
size_t size = names.size();
size_t count = 0;
int percent = 0;
for(std::set<std::string>::const_iterator
i = names.begin(); i != names.end(); ++i){
// Asking for these caches the result
m_itemdef->getInventoryTexture(*i, this);
m_itemdef->getWieldMesh(*i, this);
count++;
percent = count*100/size;
if (count%50 == 0) // only update every 50 item
draw_load_screen(text,device,font,0,percent);
}
delete[] text;
}
// Start mesh update thread after setting up content definitions

@ -385,7 +385,7 @@ public:
bool nodedefReceived()
{ return m_nodedef_received; }
void afterContentReceived();
void afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font);
float getRTT(void);

@ -91,10 +91,10 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
PROTOCOL_VERSION 19:
GENERIC_CMD_SET_PHYSICS_OVERRIDE
PROTOCOL_VERSION 20:
TOCLIENT_HUD_ADD
TOCLIENT_HUD_RM
TOCLIENT_HUD_CHANGE
TOCLIENT_HUD_BUILTIN_ENABLE
TOCLIENT_HUDADD
TOCLIENT_HUDRM
TOCLIENT_HUDCHANGE
TOCLIENT_HUD_SET_FLAGS
*/
#define LATEST_PROTOCOL_VERSION 20

@ -117,6 +117,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("selectionbox_color", "(0,0,0)");
settings->setDefault("crosshair_color", "(255,255,255)");
settings->setDefault("crosshair_alpha", "255");
settings->setDefault("mouse_sensitivity", "0.2");
settings->setDefault("enable_sound", "true");
settings->setDefault("sound_volume", "0.8");
settings->setDefault("desynchronize_mapblock_texture_animation", "true");

@ -1084,8 +1084,8 @@ void ServerEnvironment::step(float dtime)
{
v3s16 p = *i;
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") became inactive"<<std::endl;*/
/* infostream<<"Server: Block " << PP(p)
<< " became inactive"<<std::endl; */
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
@ -1105,9 +1105,6 @@ void ServerEnvironment::step(float dtime)
{
v3s16 p = *i;
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") became active"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL){
// Block needs to be fetched first
@ -1117,6 +1114,8 @@ void ServerEnvironment::step(float dtime)
}
activateBlock(block);
/* infostream<<"Server: Block " << PP(p)
<< " became active"<<std::endl; */
}
}
@ -1850,17 +1849,17 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
<<" Forcing delete."<<std::endl;
force_delete = true;
} else {
u16 new_id = pending_delete ? id : 0;
// If static counterpart already exists, remove it first.
// This shouldn't happen, but happens rarely for some
// unknown reason. Unsuccessful attempts have been made to
// find said reason.
if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
infostream<<"ServerEnv: WARNING: Performing hack #83274"
<<std::endl;
block->m_static_objects.remove(new_id);
block->m_static_objects.remove(id);
}
block->m_static_objects.insert(new_id, s_obj);
//store static data
block->m_static_objects.insert(0, s_obj);
// Only mark block as modified if data changed considerably
if(shall_be_written)

@ -395,11 +395,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
/*
Draws a screen with a single text on it.
Text will be removed when the screen is drawn the next time.
Additionally, a progressbar can be drawn when percent is set between 0 and 100.
*/
/*gui::IGUIStaticText **/
void draw_load_screen(const std::wstring &text,
video::IVideoDriver* driver, gui::IGUIFont* font)
IrrlichtDevice* device, gui::IGUIFont* font,
float dtime=0 ,int percent=0, bool clouds=true)
{
video::IVideoDriver* driver = device->getVideoDriver();
v2u32 screensize = driver->getScreenSize();
const wchar_t *loadingtext = text.c_str();
core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
@ -411,7 +414,30 @@ void draw_load_screen(const std::wstring &text,
loadingtext, textrect, false, false);
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
driver->beginScene(true, true, video::SColor(255,0,0,0));
bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
if (cloud_menu_background)
{
g_menuclouds->step(dtime*3);
g_menuclouds->render();
driver->beginScene(true, true, video::SColor(255,140,186,250));
g_menucloudsmgr->drawAll();
}
else
driver->beginScene(true, true, video::SColor(255,0,0,0));
if (percent >= 0 && percent <= 100) // draw progress bar
{
core::vector2d<s32> barsize(256,32);
core::rect<s32> barrect(center-barsize/2, center+barsize/2);
driver->draw2DRectangle(video::SColor(255,255,255,255),barrect, NULL); // border
driver->draw2DRectangle(video::SColor(255,64,64,64), core::rect<s32> (
barrect.UpperLeftCorner+1,
barrect.LowerRightCorner-1), NULL); // black inside the bar
driver->draw2DRectangle(video::SColor(255,128,128,128), core::rect<s32> (
barrect.UpperLeftCorner+1,
core::vector2d<s32>(
barrect.LowerRightCorner.X-(barsize.X-1)+percent*(barsize.X-2)/100,
barrect.LowerRightCorner.Y-1)), NULL); // the actual progress
}
guienv->drawAll();
driver->endScene();
@ -789,6 +815,67 @@ public:
}
};
void nodePlacementPrediction(Client &client,
const ItemDefinition &playeritem_def,
v3s16 nodepos, v3s16 neighbourpos)
{
std::string prediction = playeritem_def.node_placement_prediction;
INodeDefManager *nodedef = client.ndef();
ClientMap &map = client.getEnv().getClientMap();
if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
{
verbosestream<<"Node placement prediction for "
<<playeritem_def.name<<" is "
<<prediction<<std::endl;
v3s16 p = neighbourpos;
// Place inside node itself if buildable_to
try{
MapNode n_under = map.getNode(nodepos);
if(nodedef->get(n_under).buildable_to)
p = nodepos;
else if (!nodedef->get(map.getNode(p)).buildable_to)
return;
}catch(InvalidPositionException &e){}
// Find id of predicted node
content_t id;
bool found = nodedef->getId(prediction, id);
if(!found){
errorstream<<"Node placement prediction failed for "
<<playeritem_def.name<<" (places "
<<prediction
<<") - Name not known"<<std::endl;
return;
}
// Predict param2
u8 param2 = 0;
if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
v3s16 dir = nodepos - neighbourpos;
if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
param2 = dir.Y < 0 ? 1 : 0;
} else if(abs(dir.X) > abs(dir.Z)){
param2 = dir.X < 0 ? 3 : 2;
} else {
param2 = dir.Z < 0 ? 5 : 4;
}
}
// TODO: Facedir prediction
// TODO: If predicted node is in attached_node group, check attachment
// Add node to client map
MapNode n(id, 0, param2);
try{
// This triggers the required mesh update too
client.addNode(p, n);
}catch(InvalidPositionException &e){
errorstream<<"Node placement prediction failed for "
<<playeritem_def.name<<" (places "
<<prediction
<<") - Position not loaded"<<std::endl;
}
}
}
void the_game(
bool &kill,
bool random_input,
@ -821,7 +908,11 @@ void the_game(
Draw "Loading" screen
*/
draw_load_screen(L"Loading...", driver, font);
{
wchar_t* text = wgettext("Loading...");
draw_load_screen(text, device, font,0,0);
delete[] text;
}
// Create texture source
IWritableTextureSource *tsrc = createTextureSource(device);
@ -878,7 +969,9 @@ void the_game(
*/
if(address == ""){
draw_load_screen(L"Creating server...", driver, font);
wchar_t* text = wgettext("Creating server....");
draw_load_screen(text, device, font,0,25);
delete[] text;
infostream<<"Creating server"<<std::endl;
server = new Server(map_dir, configpath, gamespec,
simple_singleplayer_mode);
@ -891,7 +984,11 @@ void the_game(
Create client
*/
draw_load_screen(L"Creating client...", driver, font);
{
wchar_t* text = wgettext("Creating client...");
draw_load_screen(text, device, font,0,50);
delete[] text;
}
infostream<<"Creating client"<<std::endl;
MapDrawControl draw_control;
@ -902,7 +999,11 @@ void the_game(
// Client acts as our GameDef
IGameDef *gamedef = &client;
draw_load_screen(L"Resolving address...", driver, font);
{
wchar_t* text = wgettext("Resolving address...");
draw_load_screen(text, device, font,0,75);
delete[] text;
}
Address connect_address(0,0,0,0, port);
try{
if(address == "")
@ -934,15 +1035,26 @@ void the_game(
bool could_connect = false;
bool connect_aborted = false;
try{
float frametime = 0.033;
float time_counter = 0.0;
input->clear();
float fps_max = g_settings->getFloat("fps_max");
bool cloud_menu_background = g_settings->getBool("menu_clouds");
u32 lasttime = device->getTimer()->getTime();
while(device->run())
{
f32 dtime=0; // in seconds
if (cloud_menu_background) {
u32 time = device->getTimer()->getTime();
if(time > lasttime)
dtime = (time - lasttime) / 1000.0;
else
dtime = 0;
lasttime = time;
}
// Update client and server
client.step(frametime);
client.step(dtime);
if(server != NULL)
server->step(frametime);
server->step(dtime);
// End condition
if(client.connectedAndInitialized()){
@ -963,15 +1075,37 @@ void the_game(
}
// Display status
std::wostringstream ss;
ss<<L"Connecting to server... (press Escape to cancel)\n";
std::wstring animation = L"/-\\|";
ss<<animation[(int)(time_counter/0.2)%4];
draw_load_screen(ss.str(), driver, font);
{
wchar_t* text = wgettext("Connecting to server...");
draw_load_screen(text, device, font, dtime, 100);
delete[] text;
}
// Delay a bit
sleep_ms(1000*frametime);
time_counter += frametime;
// On some computers framerate doesn't seem to be
// automatically limited
if (cloud_menu_background) {
// Time of frame without fps limit
float busytime;
u32 busytime_u32;
// not using getRealTime is necessary for wine
u32 time = device->getTimer()->getTime();
if(time > lasttime)
busytime_u32 = time - lasttime;
else
busytime_u32 = 0;
busytime = busytime_u32 / 1000.0;
// FPS limiter
u32 frametime_min = 1000./fps_max;
if(busytime_u32 < frametime_min) {
u32 sleeptime = frametime_min - busytime_u32;
device->sleep(sleeptime);
}
} else {
sleep_ms(25);
}
time_counter += dtime;
}
}
catch(con::PeerNotFoundException &e)
@ -995,15 +1129,26 @@ void the_game(
bool got_content = false;
bool content_aborted = false;
{
float frametime = 0.033;
float time_counter = 0.0;
input->clear();
float fps_max = g_settings->getFloat("fps_max");
bool cloud_menu_background = g_settings->getBool("menu_clouds");
u32 lasttime = device->getTimer()->getTime();
while(device->run())
{
f32 dtime=0; // in seconds
if (cloud_menu_background) {
u32 time = device->getTimer()->getTime();
if(time > lasttime)
dtime = (time - lasttime) / 1000.0;
else
dtime = 0;
lasttime = time;
}
// Update client and server
client.step(frametime);
client.step(dtime);
if(server != NULL)
server->step(frametime);
server->step(dtime);
// End condition
if(client.texturesReceived() &&
@ -1025,21 +1170,52 @@ void the_game(
}
// Display status
std::wostringstream ss;
ss<<L"Waiting content... (press Escape to cancel)\n";
std::ostringstream ss;
int progress=0;
if (!client.itemdefReceived())
{
ss << "Item definitions...";
progress = 0;
}
else if (!client.nodedefReceived())
{
ss << "Node definitions...";
progress = 25;
}
else
{
ss << "Media...";
progress = 50+client.mediaReceiveProgress()*50+0.5;
}
wchar_t* text = wgettext(ss.str().c_str());
draw_load_screen(text, device, font, dtime, progress);
delete[] text;
ss<<(client.itemdefReceived()?L"[X]":L"[ ]");
ss<<L" Item definitions\n";
ss<<(client.nodedefReceived()?L"[X]":L"[ ]");
ss<<L" Node definitions\n";
ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] ";
ss<<L" Media\n";
// On some computers framerate doesn't seem to be
// automatically limited
if (cloud_menu_background) {
// Time of frame without fps limit
float busytime;
u32 busytime_u32;
// not using getRealTime is necessary for wine
u32 time = device->getTimer()->getTime();
if(time > lasttime)
busytime_u32 = time - lasttime;
else
busytime_u32 = 0;
busytime = busytime_u32 / 1000.0;
draw_load_screen(ss.str(), driver, font);
// FPS limiter
u32 frametime_min = 1000./fps_max;
// Delay a bit
sleep_ms(1000*frametime);
time_counter += frametime;
if(busytime_u32 < frametime_min) {
u32 sleeptime = frametime_min - busytime_u32;
device->sleep(sleeptime);
}
} else {
sleep_ms(25);
}
time_counter += dtime;
}
}
@ -1056,7 +1232,7 @@ void the_game(
After all content has been received:
Update cached textures, meshes and materials
*/
client.afterContentReceived();
client.afterContentReceived(device,font);
/*
Create the camera node
@ -1885,7 +2061,8 @@ void the_game(
if(input->isKeyDown(irr::KEY_RIGHT))
dx += dtime * keyspeed;*/
float d = 0.2;
float d = g_settings->getFloat("mouse_sensitivity");
d = rangelim(d, 0.01, 100.0);
camera_yaw -= dx*d;
camera_pitch += dy*d;
if(camera_pitch < -89.5) camera_pitch = -89.5;
@ -2198,17 +2375,15 @@ void the_game(
- Can it point to liquids?
*/
ItemStack playeritem;
bool playeritem_usable = false;
bool playeritem_liquids_pointable = false;
{
InventoryList *mlist = local_inventory.getList("main");
if(mlist != NULL)
{
playeritem = mlist->getItem(client.getPlayerItem());
playeritem_usable = playeritem.getDefinition(itemdef).usable;
playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable;
}
}
const ItemDefinition &playeritem_def =
playeritem.getDefinition(itemdef);
ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef);
@ -2267,7 +2442,7 @@ void the_game(
// input
&client, player_position, camera_direction,
camera_position, shootline, d,
playeritem_liquids_pointable, !ldown_for_dig,
playeritem_def.liquids_pointable, !ldown_for_dig,
// output
hilightboxes,
selected_object);
@ -2327,7 +2502,7 @@ void the_game(
else
repeat_rightclick_timer = 0;
if(playeritem_usable && input->getLeftState())
if(playeritem_def.usable && input->getLeftState())
{
if(input->getLeftClicked())
client.interact(4, pointed);
@ -2534,46 +2709,13 @@ void the_game(
// If the wielded item has node placement prediction,
// make that happen
const ItemDefinition &def =
playeritem.getDefinition(itemdef);
if(def.node_placement_prediction != ""
&& !nodedef->get(map.getNode(nodepos)).rightclickable)
do{ // breakable
verbosestream<<"Node placement prediction for "
<<playeritem.name<<" is "
<<def.node_placement_prediction<<std::endl;
v3s16 p = neighbourpos;
// Place inside node itself if buildable_to
try{
MapNode n_under = map.getNode(nodepos);
if(nodedef->get(n_under).buildable_to)
p = nodepos;
}catch(InvalidPositionException &e){}
// Find id of predicted node
content_t id;
bool found =
nodedef->getId(def.node_placement_prediction, id);
if(!found){
errorstream<<"Node placement prediction failed for "
<<playeritem.name<<" (places "
<<def.node_placement_prediction
<<") - Name not known"<<std::endl;
break;
}
MapNode n(id);
try{
// This triggers the required mesh update too
client.addNode(p, n);
}catch(InvalidPositionException &e){
errorstream<<"Node placement prediction failed for "
<<playeritem.name<<" (places "
<<def.node_placement_prediction
<<") - Position not loaded"<<std::endl;
}
}while(0);
nodePlacementPrediction(client,
playeritem_def,
nodepos, neighbourpos);
// Read the sound
soundmaker.m_player_rightpunch_sound = def.sound_place;
soundmaker.m_player_rightpunch_sound =
playeritem_def.sound_place;
}
}
}
@ -3193,6 +3335,8 @@ void the_game(
clouds->drop();
if (gui_chat_console)
gui_chat_console->drop();
if (sky)
sky->drop();
clear_particles();
/*
@ -3201,7 +3345,9 @@ void the_game(
*/
{
/*gui::IGUIStaticText *gui_shuttingdowntext = */
draw_load_screen(L"Shutting down stuff...", driver, font);
wchar_t* text = wgettext("Shutting down stuff...");
draw_load_screen(text, device, font, 0, -1, false);
delete[] text;
/*driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll();
driver->endScene();

@ -121,6 +121,7 @@ GUIChatConsole::GUIChatConsole(
GUIChatConsole::~GUIChatConsole()
{
delete m_font;
}
void GUIChatConsole::openConsole(f32 height)

@ -407,14 +407,26 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event)
delete[] text;
menu->drop();
ModConfiguration modconf(m_wspec.path);
if(!modconf.isConsistent())
try
{
wchar_t* text = wgettext("Warning: Configuration not consistent. ");
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 );
delete[] text;
text );
menu->drop();
}

@ -42,13 +42,22 @@ GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
CreateWorldDest *dest,
const std::vector<SubgameSpec> &games
const std::vector<SubgameSpec> &games,
const std::string &initial_game
):
GUIModalMenu(env, parent, id, menumgr),
m_dest(dest),
m_games(games)
m_games(games),
m_initial_game_i(0)
{
assert(games.size() > 0);
for(size_t i=0; i<games.size(); i++){
if(games[i].id == initial_game){
m_initial_game_i = i;
break;
}
}
}
GUICreateWorld::~GUICreateWorld()
@ -151,7 +160,7 @@ void GUICreateWorld::regenerateGui(v2u32 screensize)
os<<L"]";
e->addItem(os.str().c_str());
}
e->setSelected(0);
e->setSelected(m_initial_game_i);
}
changeCtype("");
{

@ -38,7 +38,8 @@ public:
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
CreateWorldDest *dest,
const std::vector<SubgameSpec> &games);
const std::vector<SubgameSpec> &games,
const std::string &initial_game);
~GUICreateWorld();
void removeChildren();
@ -56,6 +57,7 @@ public:
private:
CreateWorldDest *m_dest;
std::vector<SubgameSpec> m_games;
int m_initial_game_i;
};
#endif

@ -53,12 +53,12 @@ const wchar_t *contrib_core_strs[] = {
L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
L"proller <proler@gmail.com>"
L"proller <proler@gmail.com>",
L"sfan5 <sfan5@live.de>"
};
const wchar_t *contrib_active_strs[] = {
L"kahrl <kahrl@gmx.net>",
L"sfan5 <sfan5@live.de>",
L"sapier <sapier@gmx.net>",
L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
@ -162,15 +162,8 @@ enum
GUI_ID_SERVERLIST_TOGGLE,
GUI_ID_SERVERLIST_DELETE,
GUI_ID_SERVERLIST_TITLE,
};
enum
{
TAB_SINGLEPLAYER=0,
TAB_MULTIPLAYER,
TAB_ADVANCED,
TAB_SETTINGS,
TAB_CREDITS
GUI_ID_GAME_BUTTON_FIRST = 130,
GUI_ID_GAME_BUTTON_MAX = 150,
};
GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
@ -255,8 +248,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, size.X, 40);
rect += v2s32(4, 0);
Environment->addStaticText(narrow_to_wide(
"Minetest " VERSION_STRING).c_str(),
std::string t = "Minetest " VERSION_STRING;
if(m_data->selected_game_name != "" &&
m_data->selected_tab == TAB_SINGLEPLAYER){
t += "/";
t += m_data->selected_game_name;
}
Environment->addStaticText(narrow_to_wide(t).c_str(),
rect, false, true, this, -1);
}
@ -342,11 +340,17 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
gui::IGUIListBox *e = Environment->addListBox(rect, this,
GUI_ID_WORLD_LISTBOX);
e->setDrawBackground(true);
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
i != m_data->worlds.end(); i++){
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
m_world_indices.clear();
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
const WorldSpec &spec = m_data->worlds[wi];
if(spec.gameid == m_data->selected_game){
//e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
e->addItem(narrow_to_wide(spec.name).c_str());
m_world_indices.push_back(wi);
if(m_data->selected_world == (int)wi)
e->setSelected(m_world_indices.size()-1);
}
}
e->setSelected(m_data->selected_world);
Environment->setFocus(e);
}
// Delete world button
@ -416,6 +420,26 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
delete[] text;
}
changeCtype("C");
/* Add game selection buttons */
video::IVideoDriver* driver = Environment->getVideoDriver();
for(size_t i=0; i<m_data->games.size(); i++){
const SubgameSpec *spec = &m_data->games[i];
v2s32 p(8 + i*(48+8), screensize.Y - (48+8));
core::rect<s32> rect(0, 0, 48, 48);
rect += p;
video::ITexture *bgtexture = NULL;
if(spec->menuicon_path != "")
bgtexture = driver->getTexture(spec->menuicon_path.c_str());
gui::IGUIButton *b = Environment->addButton(rect, this,
GUI_ID_GAME_BUTTON_FIRST+i, narrow_to_wide(wrap_rows(spec->id, 4)).c_str());
if(bgtexture){
b->setImage(bgtexture);
b->setText(L"");
b->setDrawBorder(false);
b->setUseAlphaChannel(true);
}
}
}
else if(m_data->selected_tab == TAB_MULTIPLAYER)
{
@ -471,16 +495,20 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 390, 20);
rect += m_topleft_client + v2s32(50, 10);
Environment->addStaticText(wgettext("Favorites:"),
wchar_t* text = wgettext("Favorites:");
Environment->addStaticText(text,
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
delete[] text;
}
} else {
m_data->servers = ServerList::getOnline();
{
core::rect<s32> rect(0, 0, 390, 20);
rect += m_topleft_client + v2s32(50, 10);
Environment->addStaticText(wgettext("Public Server List:"),
wchar_t* text = wgettext("Public Server List:");
Environment->addStaticText(text,
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
delete[] text;
}
}
#else
@ -488,8 +516,10 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 390, 20);
rect += m_topleft_client + v2s32(50, 10);
Environment->addStaticText(wgettext("Favorites:"),
wchar_t* text = wgettext("Favorites:");
Environment->addStaticText(text,
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
delete[] text;
}
#endif
updateGuiServerList();
@ -714,9 +744,11 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
gui::IGUIListBox *e = Environment->addListBox(rect, this,
GUI_ID_WORLD_LISTBOX);
e->setDrawBackground(true);
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
i != m_data->worlds.end(); i++){
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
m_world_indices.clear();
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
const WorldSpec &spec = m_data->worlds[wi];
e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
m_world_indices.push_back(wi);
}
e->setSelected(m_data->selected_world);
}
@ -910,6 +942,8 @@ void GUIMainMenu::drawMenu()
return;
video::IVideoDriver* driver = Environment->getVideoDriver();
/* Draw menu background */
/*video::SColor bgcolor(140,0,0,0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
@ -976,6 +1010,8 @@ void GUIMainMenu::drawMenu()
}
}
/* Draw UI elements */
gui::IGUIElement::draw();
}
@ -1100,8 +1136,13 @@ void GUIMainMenu::readInput(MainMenuData *dst)
{
gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX){
int list_i = ((gui::IGUIListBox*)e)->getSelected();
if(list_i == -1)
dst->selected_world = -1;
else
dst->selected_world = m_world_indices[list_i];
}
}
{
ServerListSpec server =
@ -1221,7 +1262,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
return true;
}
case GUI_ID_CREATE_WORLD_BUTTON: {
std::vector<SubgameSpec> games = getAvailableGames();
const std::vector<SubgameSpec> &games = m_data->games;
if(games.size() == 0){
wchar_t* text = wgettext("Cannot create world: No games found");
GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
@ -1232,7 +1273,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
} else {
CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
menumgr, dest, games);
menumgr, dest, games, m_data->selected_game);
menu->drop();
}
return true;
@ -1308,6 +1349,16 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
}
#endif
}
/* Game buttons */
int eid = event.GUIEvent.Caller->getID();
if(eid >= GUI_ID_GAME_BUTTON_FIRST &&
eid <= GUI_ID_GAME_BUTTON_MAX){
m_data->selected_game =
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].id;
m_data->selected_game_name =
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].name;
regenerateGui(m_screensize_old);
}
}
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
{
@ -1318,9 +1369,11 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
readInput(&cur);
if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
{
wchar_t* text = wgettext("Address required.");
(new GUIMessageMenu(env, parent, -1, menumgr,
wgettext("Address required."))
text)
)->drop();
delete[] text;
return true;
}
acceptInput();
@ -1328,6 +1381,10 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
return true;
}
}
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)
{
readInput(m_data);
}
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
{
switch(event.GUIEvent.Caller->getID())

@ -34,11 +34,22 @@ enum {
SERVERLIST_PUBLIC,
};
enum
{
TAB_SINGLEPLAYER=0,
TAB_MULTIPLAYER,
TAB_ADVANCED,
TAB_SETTINGS,
TAB_CREDITS
};
struct MainMenuData
{
// These are in the native format of the gui elements
// Generic
int selected_tab;
std::string selected_game;
std::string selected_game_name;
// Client options
std::string servername;
std::string serverdescription;
@ -78,6 +89,8 @@ struct MainMenuData
MainMenuData():
// Generic
selected_tab(0),
selected_game("minetest"),
selected_game_name("Minetest"),
// Client opts
fancy_trees(false),
smooth_lighting(false),
@ -128,6 +141,8 @@ private:
s32 id;
IMenuManager *menumgr;
std::vector<int> m_world_indices;
bool m_is_regenerating;
v2s32 m_topleft_client;
v2s32 m_size_client;

@ -168,16 +168,16 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, 180, 240);
rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
wchar_t* text = wgettext("Default Controls:\n"
"- WASD: Walk\n"
"- Mouse left: dig/hit\n"
"- WASD: move\n"
"- Space: jump/climb\n"
"- Shift: sneak/go down\n"
"- Q: drop item\n"
"- I: inventory\n"
"- Mouse: turn/look\n"
"- Mouse left: dig/punch\n"
"- Mouse right: place/use\n"
"- Mouse wheel: select item\n"
"- 0...9: select item\n"
"- Shift: sneak\n"
"- R: Toggle viewing all loaded chunks\n"
"- I: Inventory menu\n"
"- ESC: This menu\n"
"- T: Chat\n"
"- T: chat\n"
);
Environment->addStaticText(text, rect, false, true, this, 258);
delete[] text;

@ -226,17 +226,11 @@ class CItemDefManager: public IWritableItemDefManager
public:
CItemDefManager()
{
for (std::map<std::string, ItemDefinition*>::iterator iter =
m_item_definitions.begin(); iter != m_item_definitions.end();
iter ++) {
delete iter->second;
}
m_item_definitions.clear();
#ifndef SERVER
m_main_thread = get_current_thread_id();
m_driver = NULL;
#endif
clear();
}
virtual ~CItemDefManager()
@ -260,6 +254,12 @@ public:
}
m_driver = NULL;
#endif
for (std::map<std::string, ItemDefinition*>::iterator iter =
m_item_definitions.begin(); iter != m_item_definitions.end();
iter ++) {
delete iter->second;
}
m_item_definitions.clear();
}
virtual const ItemDefinition& get(const std::string &name_) const
{

@ -76,6 +76,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "subgame.h"
#include "quicktune.h"
#include "serverlist.h"
#include "sound.h"
#include "sound_openal.h"
/*
Settings.
@ -88,6 +90,10 @@ Settings *g_settings = &main_settings;
Profiler main_profiler;
Profiler *g_profiler = &main_profiler;
// Menu clouds are created later
Clouds *g_menuclouds = 0;
irr::scene::ISceneManager *g_menucloudsmgr = 0;
/*
Debug streams
*/
@ -196,7 +202,49 @@ u32 getTime(TimePrecision prec) {
return 0;
return g_timegetter->getTime(prec);
}
#endif
//Client side main menu music fetcher
#ifndef SERVER
class MenuMusicFetcher: public OnDemandSoundFetcher
{
std::set<std::string> m_fetched;
public:
void fetchSounds(const std::string &name,
std::set<std::string> &dst_paths,
std::set<std::string> &dst_datas)
{
if(m_fetched.count(name))
return;
m_fetched.insert(name);
std::string base;
base = porting::path_share + DIR_DELIM + "sounds";
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
base = porting::path_user + DIR_DELIM + "sounds";
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
}
};
#endif
class StderrLogOutput: public ILogOutput
@ -612,122 +660,181 @@ private:
bool rightreleased;
};
//Draw the tiled menu background
void drawMenuBackground(video::IVideoDriver* driver) {
core::dimension2d<u32> screensize = driver->getScreenSize();
struct MenuTextures
{
std::string current_gameid;
bool global_textures;
video::ITexture *background;
video::ITexture *overlay;
video::ITexture *header;
video::ITexture *footer;
std::string path = getTexturePath("menubg.png");
if (path[0]) {
static const video::ITexture *bgtexture =
driver->getTexture(path.c_str());
MenuTextures():
global_textures(false),
background(NULL),
overlay(NULL),
header(NULL),
footer(NULL)
{}
if (bgtexture) {
s32 scaledsize = 128;
// The important difference between destsize and screensize is
// that destsize is rounded to whole scaled pixels.
// These formulas use component-wise multiplication and division of v2u32.
v2u32 texturesize = bgtexture->getSize();
v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
v2u32 destsize = scaledsize * sourcesize / texturesize;
// Default texture wrapping mode in Irrlicht is ETC_REPEAT.
driver->draw2DImage(bgtexture,
core::rect<s32>(0, 0, destsize.X, destsize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
static video::ITexture* getMenuTexture(const std::string &tname,
video::IVideoDriver* driver, const SubgameSpec *spec)
{
if(spec){
std::string path;
// eg. minetest_menu_background.png (for texture packs)
std::string pack_tname = spec->id + "_menu_" + tname + ".png";
path = getTexturePath(pack_tname);
if(path != "")
return driver->getTexture(path.c_str());
// eg. games/minetest_game/menu/background.png
path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
if(path != "")
return driver->getTexture(path.c_str());
} else {
std::string path;
// eg. menu_background.png
std::string pack_tname = "menu_" + tname + ".png";
path = getTexturePath(pack_tname);
if(path != "")
return driver->getTexture(path.c_str());
}
return NULL;
}
void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
{
if(tab == TAB_SINGLEPLAYER){
if(spec->id == current_gameid)
return;
current_gameid = spec->id;
global_textures = false;
background = getMenuTexture("background", driver, spec);
overlay = getMenuTexture("overlay", driver, spec);
header = getMenuTexture("header", driver, spec);
footer = getMenuTexture("footer", driver, spec);
} else {
if(global_textures)
return;
current_gameid = "";
global_textures = true;
background = getMenuTexture("background", driver, NULL);
overlay = getMenuTexture("overlay", driver, NULL);
header = getMenuTexture("header", driver, NULL);
footer = getMenuTexture("footer", driver, NULL);
}
}
};
void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
{
v2u32 screensize = driver->getScreenSize();
video::ITexture *texture = menutextures.background;
/* If no texture, draw background of solid color */
if(!texture){
video::SColor color(255,80,58,37);
core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
driver->draw2DRectangle(color, rect, NULL);
return;
}
/* Draw background texture */
v2u32 sourcesize = texture->getSize();
driver->draw2DImage(texture,
core::rect<s32>(0, 0, screensize.X, screensize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
}
void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
{
v2u32 screensize = driver->getScreenSize();
video::ITexture *texture = menutextures.overlay;
/* If no texture, draw nothing */
if(!texture)
return;
/* Draw overlay texture */
v2u32 sourcesize = texture->getSize();
driver->draw2DImage(texture,
core::rect<s32>(0, 0, screensize.X, screensize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
}
void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
{
core::dimension2d<u32> screensize = driver->getScreenSize();
video::ITexture *texture = menutextures.header;
/* If no texture, draw nothing */
if(!texture)
return;
f32 mult = (((f32)screensize.Width / 2)) /
((f32)texture->getOriginalSize().Width);
v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
((f32)texture->getOriginalSize().Height) * mult);
// Don't draw the header is there isn't enough room
s32 free_space = (((s32)screensize.Height)-320)/2;
if (free_space > splashsize.Y) {
core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
((free_space/2)-splashsize.Y/2)+10);
video::SColor bgcolor(255,50,50,50);
driver->draw2DImage(texture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getSize())),
NULL, NULL, true);
}
}
//Draw the footer at the bottom of the window
void drawMenuFooter(video::IVideoDriver* driver, bool clouds) {
void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
{
core::dimension2d<u32> screensize = driver->getScreenSize();
std::string path = getTexturePath(clouds ?
"menufooter_clouds.png" : "menufooter.png");
if (path[0]) {
static const video::ITexture *footertexture =
driver->getTexture(path.c_str());
video::ITexture *texture = menutextures.footer;
if (footertexture) {
f32 mult = (((f32)screensize.Width)) /
((f32)footertexture->getOriginalSize().Width);
/* If no texture, draw nothing */
if(!texture)
return;
v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult,
((f32)footertexture->getOriginalSize().Height) * mult);
f32 mult = (((f32)screensize.Width)) /
((f32)texture->getOriginalSize().Width);
// Don't draw the footer if there isn't enough room
s32 free_space = (((s32)screensize.Height)-320)/2;
if (free_space > footersize.Y) {
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
rect -= v2s32(footersize.X/2, 0);
v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
((f32)texture->getOriginalSize().Height) * mult);
driver->draw2DImage(footertexture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(footertexture->getSize())),
NULL, NULL, true);
}
}
// Don't draw the footer if there isn't enough room
s32 free_space = (((s32)screensize.Height)-320)/2;
if (free_space > footersize.Y) {
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
rect -= v2s32(footersize.X/2, 0);
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getSize())),
NULL, NULL, true);
}
}
// Draw the Header over the main menu
void drawMenuHeader(video::IVideoDriver* driver) {
core::dimension2d<u32> screensize = driver->getScreenSize();
std::string path = getTexturePath("menuheader.png");
if (path[0]) {
static const video::ITexture *splashtexture =
driver->getTexture(path.c_str());
if(splashtexture) {
f32 mult = (((f32)screensize.Width / 2)) /
((f32)splashtexture->getOriginalSize().Width);
v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult,
((f32)splashtexture->getOriginalSize().Height) * mult);
// Don't draw the header is there isn't enough room
s32 free_space = (((s32)screensize.Height)-320)/2;
if (free_space > splashsize.Y) {
core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
((free_space/2)-splashsize.Y/2)+10);
video::SColor bgcolor(255,50,50,50);
driver->draw2DImage(splashtexture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(splashtexture->getSize())),
NULL, NULL, true);
}
static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
{
for(size_t i=0; i<menudata.games.size(); i++){
if(menudata.games[i].id == menudata.selected_game){
return &menudata.games[i];
}
}
return NULL;
}
// Draw the Splash over the clouds and under the main menu
void drawMenuSplash(video::IVideoDriver* driver) {
core::dimension2d<u32> screensize = driver->getScreenSize();
std::string path = getTexturePath("menusplash.png");
if (path[0]) {
static const video::ITexture *splashtexture =
driver->getTexture(path.c_str());
if(splashtexture) {
core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height);
video::SColor bgcolor(255,50,50,50);
driver->draw2DImage(splashtexture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(splashtexture->getSize())),
NULL, NULL, true);
}
}
}
#endif
#endif // !SERVER
// These are defined global so that they're not optimized too much.
// Can't change them to volatile.
@ -900,6 +1007,8 @@ int main(int argc, char *argv[])
allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
_("Set gameid (\"--gameid list\" prints available ones)"))));
#ifndef SERVER
allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
_("Show available video modes"))));
allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
_("Run speed tests"))));
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
@ -1379,11 +1488,64 @@ int main(int argc, char *argv[])
}
/*
Create device and exit if creation failed
List video modes if requested
*/
MyEventReceiver receiver;
if(cmd_args.getFlag("videomodes")){
IrrlichtDevice *nulldevice;
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
params.DriverType = video::EDT_NULL;
params.WindowSize = core::dimension2d<u32>(640, 480);
params.Bits = 24;
params.AntiAlias = fsaa;
params.Fullscreen = false;
params.Stencilbuffer = false;
params.Vsync = vsync;
params.EventReceiver = &receiver;
nulldevice = createDeviceEx(params);
if(nulldevice == 0)
return 1;
dstream<<_("Available video modes (WxHxD):")<<std::endl;
video::IVideoModeList *videomode_list =
nulldevice->getVideoModeList();
if(videomode_list == 0){
nulldevice->drop();
return 1;
}
s32 videomode_count = videomode_list->getVideoModeCount();
core::dimension2d<u32> videomode_res;
s32 videomode_depth;
for (s32 i = 0; i < videomode_count; ++i){
videomode_res = videomode_list->getVideoModeResolution(i);
videomode_depth = videomode_list->getVideoModeDepth(i);
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
<<"x"<<videomode_depth<<std::endl;
}
dstream<<_("Active video mode (WxHxD):")<<std::endl;
videomode_res = videomode_list->getDesktopResolution();
videomode_depth = videomode_list->getDesktopDepth();
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
<<"x"<<videomode_depth<<std::endl;
nulldevice->drop();
return 0;
}
/*
Create device and exit if creation failed
*/
IrrlichtDevice *device;
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
@ -1426,6 +1588,7 @@ int main(int argc, char *argv[])
{
dstream<<"Running speed tests"<<std::endl;
SpeedTests();
device->drop();
return 0;
}
@ -1477,6 +1640,19 @@ int main(int argc, char *argv[])
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
#endif
// Create the menu clouds
if (!g_menucloudsmgr)
g_menucloudsmgr = smgr->createNewSceneManager();
if (!g_menuclouds)
g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
g_menucloudsmgr, -1, rand(), 100);
g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255));
scene::ICameraSceneNode* camera;
camera = g_menucloudsmgr->addCameraSceneNode(0,
v3f(0,0,0), v3f(0, 60, 100));
camera->setFarValue(10000);
/*
GUI stuff
*/
@ -1558,6 +1734,10 @@ int main(int argc, char *argv[])
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
if(g_settings->exists("selected_serverlist"))
menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
if(g_settings->exists("selected_mainmenu_game")){
menudata.selected_game = g_settings->get("selected_mainmenu_game");
menudata.selected_game_name = findSubgame(menudata.selected_game).name;
}
menudata.address = narrow_to_wide(address);
menudata.name = narrow_to_wide(playername);
menudata.port = narrow_to_wide(itos(port));
@ -1611,6 +1791,17 @@ int main(int argc, char *argv[])
}
// Copy worldspecs to menu
menudata.worlds = worldspecs;
// Get game listing
menudata.games = getAvailableGames();
// If selected game doesn't exist, take first from list
if(findSubgame(menudata.selected_game).id == "" &&
!menudata.games.empty()){
menudata.selected_game = menudata.games[0].id;
}
const SubgameSpec *menugame = getMenuGame(menudata);
MenuTextures menutextures;
menutextures.update(driver, menugame, menudata.selected_tab);
if(skip_main_menu == false)
{
@ -1623,7 +1814,7 @@ int main(int argc, char *argv[])
break;
driver->beginScene(true, true,
video::SColor(255,128,128,128));
drawMenuBackground(driver);
drawMenuBackground(driver, menutextures);
guienv->drawAll();
driver->endScene();
// On some computers framerate doesn't seem to be
@ -1637,22 +1828,6 @@ int main(int argc, char *argv[])
&g_menumgr, &menudata, g_gamecallback);
menu->allowFocusRemoval(true);
// Clouds for the main menu
bool cloud_menu_background = false;
Clouds *clouds = NULL;
if (g_settings->getBool("menu_clouds")) {
cloud_menu_background = true;
clouds = new Clouds(smgr->getRootSceneNode(),
smgr, -1, rand(), 100);
clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
// A camera to see the clouds
scene::ICameraSceneNode* camera;
camera = smgr->addCameraSceneNode(0,
v3f(0,0,0), v3f(0, 60, 100));
camera->setFarValue(10000);
}
if(error_message != L"")
{
verbosestream<<"error_message = "
@ -1668,6 +1843,16 @@ int main(int argc, char *argv[])
// Time is in milliseconds, for clouds
u32 lasttime = device->getTimer()->getTime();
MenuMusicFetcher soundfetcher;
ISoundManager *sound = NULL;
sound = createOpenALSoundManager(&soundfetcher);
if(!sound)
sound = &dummySoundManager;
SimpleSoundSpec spec;
spec.name = "main_menu";
spec.gain = 1;
s32 handle = sound->playSound(spec, true);
infostream<<"Created main menu"<<std::endl;
while(device->run() && kill == false)
@ -1675,8 +1860,25 @@ int main(int argc, char *argv[])
if(menu->getStatus() == true)
break;
// Game can be selected in the menu
menugame = getMenuGame(menudata);
menutextures.update(driver, menugame, menu->getTab());
// Clouds for the main menu
bool cloud_menu_background = g_settings->getBool("menu_clouds");
if(menugame){
// If game has regular background and no overlay, don't use clouds
if(cloud_menu_background && menutextures.background &&
!menutextures.overlay){
cloud_menu_background = false;
}
// If game game has overlay and no regular background, always draw clouds
else if(menutextures.overlay && !menutextures.background){
cloud_menu_background = true;
}
}
// Time calc for the clouds
f32 dtime; // in seconds
f32 dtime=0; // in seconds
if (cloud_menu_background) {
u32 time = device->getTimer()->getTime();
if(time > lasttime)
@ -1691,15 +1893,16 @@ int main(int argc, char *argv[])
if (cloud_menu_background) {
// *3 otherwise the clouds would move very slowly
clouds->step(dtime*3);
clouds->render();
smgr->drawAll();
drawMenuSplash(driver);
drawMenuFooter(driver, true);
drawMenuHeader(driver);
g_menuclouds->step(dtime*3);
g_menuclouds->render();
g_menucloudsmgr->drawAll();
drawMenuOverlay(driver, menutextures);
drawMenuHeader(driver, menutextures);
drawMenuFooter(driver, menutextures);
} else {
drawMenuBackground(driver);
drawMenuFooter(driver, false);
drawMenuBackground(driver, menutextures);
drawMenuHeader(driver, menutextures);
drawMenuFooter(driver, menutextures);
}
guienv->drawAll();
@ -1731,14 +1934,15 @@ int main(int argc, char *argv[])
sleep_ms(25);
}
}
sound->stopSound(handle);
if(sound != &dummySoundManager){
delete sound;
sound = NULL;
}
infostream<<"Dropping main menu"<<std::endl;
menu->drop();
if (cloud_menu_background) {
clouds->drop();
smgr->clear();
}
}
playername = wide_to_narrow(menudata.name);
@ -1755,6 +1959,7 @@ int main(int argc, char *argv[])
// Save settings
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
g_settings->set("selected_mainmenu_game", menudata.selected_game);
g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
@ -1832,6 +2037,7 @@ int main(int argc, char *argv[])
continue;
}
g_settings->set("selected_world_path", path);
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
continue;
}
@ -1897,6 +2103,7 @@ int main(int argc, char *argv[])
gamespec,
simple_singleplayer_mode
);
smgr->clear();
} //try
catch(con::PeerNotFoundException &e)
@ -1927,6 +2134,10 @@ int main(int argc, char *argv[])
}
} // Menu-game loop
g_menuclouds->drop();
g_menucloudsmgr->drop();
delete input;
/*
@ -1934,6 +2145,8 @@ int main(int argc, char *argv[])
*/
device->drop();
delete font;
#endif // !SERVER
// Update configuration file

@ -28,6 +28,14 @@ extern Settings *g_settings;
class Profiler;
extern Profiler *g_profiler;
// Menu clouds
class Clouds;
extern Clouds *g_menuclouds;
// Scene manager used for menu clouds
namespace irr{namespace scene{class ISceneManager;}}
extern irr::scene::ISceneManager *g_menucloudsmgr;
// Debug streams
#include <fstream>

@ -1338,12 +1338,20 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices)
{
if(numIndices > 65535)
{
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
PreMeshBuffer *p = NULL;
for(u32 i=0; i<prebuffers.size(); i++)
{
PreMeshBuffer &pp = prebuffers[i];
if(pp.tile != tile)
continue;
if(pp.indices.size() + numIndices > 65535)
continue;
p = &pp;
break;
@ -1361,11 +1369,6 @@ void MeshCollector::append(const TileSpec &tile,
for(u32 i=0; i<numIndices; i++)
{
u32 j = indices[i] + vertex_count;
if(j > 65535)
{
dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
// NOTE: Fix is to just add an another MeshBuffer
}
p->indices.push_back(j);
}
for(u32 i=0; i<numVertices; i++)

@ -284,7 +284,6 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
if (img2 != NULL)
{
img1->copyTo(img2);
img1->drop();
mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());
img2->unlock();

@ -24,9 +24,74 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "subgame.h"
#include "settings.h"
#include "strfnd.h"
#include <cctype>
std::map<std::string, ModSpec> getModsInPath(std::string path)
static bool parseDependsLine(std::istream &is,
std::string &dep, std::set<char> &symbols)
{
std::getline(is, dep);
dep = trim(dep);
symbols.clear();
size_t pos = dep.size();
while(pos > 0 && !string_allowed(dep.substr(pos-1, 1), MODNAME_ALLOWED_CHARS)){
// last character is a symbol, not part of the modname
symbols.insert(dep[pos-1]);
--pos;
}
dep = trim(dep.substr(0, pos));
return dep != "";
}
void parseModContents(ModSpec &spec)
{
// NOTE: this function works in mutual recursion with getModsInPath
spec.depends.clear();
spec.optdepends.clear();
spec.is_modpack = false;
spec.modpack_content.clear();
// Handle modpacks (defined by containing modpack.txt)
std::ifstream modpack_is((spec.path+DIR_DELIM+"modpack.txt").c_str());
if(modpack_is.good()){ //a modpack, recursively get the mods in it
modpack_is.close(); // We don't actually need the file
spec.is_modpack = true;
spec.modpack_content = getModsInPath(spec.path, true);
// modpacks have no dependencies; they are defined and
// tracked separately for each mod in the modpack
}
else{ // not a modpack, parse the dependencies
std::ifstream is((spec.path+DIR_DELIM+"depends.txt").c_str());
while(is.good()){
std::string dep;
std::set<char> symbols;
if(parseDependsLine(is, dep, symbols)){
if(symbols.count('?') != 0){
spec.optdepends.insert(dep);
}
else{
spec.depends.insert(dep);
}
}
}
// FIXME: optdepends.txt is deprecated
// remove this code at some point in the future
std::ifstream is2((spec.path+DIR_DELIM+"optdepends.txt").c_str());
while(is2.good()){
std::string dep;
std::set<char> symbols;
if(parseDependsLine(is2, dep, symbols))
spec.optdepends.insert(dep);
}
}
}
std::map<std::string, ModSpec> getModsInPath(std::string path, bool part_of_modpack)
{
// NOTE: this function works in mutual recursion with parseModContents
std::map<std::string, ModSpec> result;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
for(u32 j=0; j<dirlist.size(); j++){
@ -39,38 +104,34 @@ std::map<std::string, ModSpec> getModsInPath(std::string path)
continue;
std::string modpath = path + DIR_DELIM + modname;
// Handle modpacks (defined by containing modpack.txt)
std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(),
std::ios_base::binary);
if(modpack_is.good()) //a modpack, recursively get the mods in it
{
modpack_is.close(); // We don't actually need the file
ModSpec spec(modname,modpath);
spec.modpack_content = getModsInPath(modpath);
spec.is_modpack = true;
result.insert(std::make_pair(modname,spec));
}
else // not a modpack, add the modspec
{
std::set<std::string> depends;
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
std::ios_base::binary);
while(is.good())
{
std::string dep;
std::getline(is, dep);
dep = trim(dep);
if(dep != "")
depends.insert(dep);
}
ModSpec spec(modname, modpath, depends);
result.insert(std::make_pair(modname,spec));
}
ModSpec spec(modname, modpath);
spec.part_of_modpack = part_of_modpack;
parseModContents(spec);
result.insert(std::make_pair(modname, spec));
}
return result;
}
ModSpec findCommonMod(const std::string &modname)
{
// Try to find in {$user,$share}/games/common/$modname
std::vector<std::string> find_paths;
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
for(u32 i=0; i<find_paths.size(); i++){
const std::string &try_path = find_paths[i];
if(fs::PathExists(try_path)){
ModSpec spec(modname, try_path);
parseModContents(spec);
return spec;
}
}
// Failed to find mod
return ModSpec();
}
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
{
std::map<std::string, ModSpec> result;
@ -109,109 +170,18 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
}
else //not a modpack
{
// infostream << "inserting mod " << mod.name << std::endl;
result.push_back(mod);
}
}
return result;
}
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
std::set<std::string> exclude_mod_names)
{
std::vector<ModSpec> result;
for(std::vector<ModSpec>::iterator it = mods.begin();
it != mods.end(); ++it)
{
ModSpec& mod = *it;
if(exclude_mod_names.count(mod.name) == 0)
result.push_back(mod);
}
return result;
}
void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
{
addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
}
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
{
// Step 1: remove mods in sorted_mods from unmet dependencies
// of new_mods. new mods without unmet dependencies are
// temporarily stored in satisfied_mods
std::vector<ModSpec> satisfied_mods;
for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
it != m_sorted_mods.end(); ++it)
{
ModSpec mod = *it;
for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
it_new != new_mods.end(); ++it_new)
{
ModSpec& mod_new = *it_new;
//infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
mod_new.unsatisfied_depends.erase(mod.name);
}
}
// split new mods into satisfied and unsatisfied
for(std::vector<ModSpec>::iterator it = new_mods.begin();
it != new_mods.end(); ++it)
{
ModSpec mod_new = *it;
if(mod_new.unsatisfied_depends.empty())
satisfied_mods.push_back(mod_new);
else
m_unsatisfied_mods.push_back(mod_new);
}
// Step 2: mods without unmet dependencies can be appended to
// the sorted list.
while(!satisfied_mods.empty())
{
ModSpec mod = satisfied_mods.back();
m_sorted_mods.push_back(mod);
satisfied_mods.pop_back();
for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
it != m_unsatisfied_mods.end(); )
{
ModSpec& mod2 = *it;
mod2.unsatisfied_depends.erase(mod.name);
if(mod2.unsatisfied_depends.empty())
{
satisfied_mods.push_back(mod2);
it = m_unsatisfied_mods.erase(it);
}
else
++it;
}
}
}
// If failed, returned modspec has name==""
static ModSpec findCommonMod(const std::string &modname)
{
// Try to find in {$user,$share}/games/common/$modname
std::vector<std::string> find_paths;
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
for(u32 i=0; i<find_paths.size(); i++){
const std::string &try_path = find_paths[i];
if(fs::PathExists(try_path))
return ModSpec(modname, try_path);
}
// Failed to find mod
return ModSpec();
}
ModConfiguration::ModConfiguration(std::string worldpath)
{
SubgameSpec gamespec = findWorldSubgame(worldpath);
// Add common mods without dependency handling
// Add common mods
std::map<std::string, ModSpec> common_mods;
std::vector<std::string> inexistent_common_mods;
Settings gameconf;
if(getGameConfig(gamespec.path, gameconf)){
@ -225,7 +195,7 @@ ModConfiguration::ModConfiguration(std::string worldpath)
if(spec.name.empty())
inexistent_common_mods.push_back(modname);
else
m_sorted_mods.push_back(spec);
common_mods.insert(std::make_pair(modname, spec));
}
}
}
@ -238,10 +208,11 @@ ModConfiguration::ModConfiguration(std::string worldpath)
s += " could not be found.";
throw ModError(s);
}
addMods(flattenMods(common_mods));
// Add all world mods and all game mods
addModsInPath(worldpath + DIR_DELIM + "worldmods");
// Add all game mods and all world mods
addModsInPath(gamespec.gamemods_path);
addModsInPath(worldpath + DIR_DELIM + "worldmods");
// check world.mt file for mods explicitely declared to be
// loaded or not by a load_mod_<modname> = ... line.
@ -264,7 +235,155 @@ ModConfiguration::ModConfiguration(std::string worldpath)
}
}
for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
i != gamespec.addon_mods_paths.end(); ++i)
addModsInPathFiltered((*i),exclude_mod_names);
// Collect all mods in gamespec.addon_mods_paths,
// excluding those in the set exclude_mod_names
std::vector<ModSpec> addon_mods;
for(std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
it_path != gamespec.addon_mods_paths.end(); ++it_path)
{
std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
for(std::vector<ModSpec>::iterator it = addon_mods_in_path.begin();
it != addon_mods_in_path.end(); ++it)
{
ModSpec& mod = *it;
if(exclude_mod_names.count(mod.name) == 0)
addon_mods.push_back(mod);
}
}
addMods(addon_mods);
// report on name conflicts
if(!m_name_conflicts.empty()){
std::string s = "Unresolved name conflicts for mods ";
for(std::set<std::string>::const_iterator it = m_name_conflicts.begin();
it != m_name_conflicts.end(); ++it)
{
if(it != m_name_conflicts.begin()) s += ", ";
s += std::string("\"") + (*it) + "\"";
}
s += ".";
throw ModError(s);
}
// get the mods in order
resolveDependencies();
}
void ModConfiguration::addModsInPath(std::string path)
{
addMods(flattenMods(getModsInPath(path)));
}
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
{
// Maintain a map of all existing m_unsatisfied_mods.
// Keys are mod names and values are indices into m_unsatisfied_mods.
std::map<std::string, u32> existing_mods;
for(u32 i = 0; i < m_unsatisfied_mods.size(); ++i){
existing_mods[m_unsatisfied_mods[i].name] = i;
}
// Add new mods
for(int want_from_modpack = 1; want_from_modpack >= 0; --want_from_modpack){
// First iteration:
// Add all the mods that come from modpacks
// Second iteration:
// Add all the mods that didn't come from modpacks
std::set<std::string> seen_this_iteration;
for(std::vector<ModSpec>::const_iterator it = new_mods.begin();
it != new_mods.end(); ++it){
const ModSpec &mod = *it;
if(mod.part_of_modpack != want_from_modpack)
continue;
if(existing_mods.count(mod.name) == 0){
// GOOD CASE: completely new mod.
m_unsatisfied_mods.push_back(mod);
existing_mods[mod.name] = m_unsatisfied_mods.size() - 1;
}
else if(seen_this_iteration.count(mod.name) == 0){
// BAD CASE: name conflict in different levels.
u32 oldindex = existing_mods[mod.name];
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
errorstream<<"WARNING: Mod name conflict detected: \""
<<mod.name<<"\""<<std::endl
<<"Will not load: "<<oldmod.path<<std::endl
<<"Overridden by: "<<mod.path<<std::endl;
m_unsatisfied_mods[oldindex] = mod;
// If there was a "VERY BAD CASE" name conflict
// in an earlier level, ignore it.
m_name_conflicts.erase(mod.name);
}
else{
// VERY BAD CASE: name conflict in the same level.
u32 oldindex = existing_mods[mod.name];
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
errorstream<<"WARNING: Mod name conflict detected: \""
<<mod.name<<"\""<<std::endl
<<"Will not load: "<<oldmod.path<<std::endl
<<"Will not load: "<<mod.path<<std::endl;
m_unsatisfied_mods[oldindex] = mod;
m_name_conflicts.insert(mod.name);
}
seen_this_iteration.insert(mod.name);
}
}
}
void ModConfiguration::resolveDependencies()
{
// Step 1: Compile a list of the mod names we're working with
std::set<std::string> modnames;
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
it != m_unsatisfied_mods.end(); ++it){
modnames.insert((*it).name);
}
// Step 2: get dependencies (including optional dependencies)
// of each mod, split mods into satisfied and unsatisfied
std::list<ModSpec> satisfied;
std::list<ModSpec> unsatisfied;
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
it != m_unsatisfied_mods.end(); ++it){
ModSpec mod = *it;
mod.unsatisfied_depends = mod.depends;
// check which optional dependencies actually exist
for(std::set<std::string>::iterator it_optdep = mod.optdepends.begin();
it_optdep != mod.optdepends.end(); ++it_optdep){
std::string optdep = *it_optdep;
if(modnames.count(optdep) != 0)
mod.unsatisfied_depends.insert(optdep);
}
// if a mod has no depends it is initially satisfied
if(mod.unsatisfied_depends.empty())
satisfied.push_back(mod);
else
unsatisfied.push_back(mod);
}
// Step 3: mods without unmet dependencies can be appended to
// the sorted list.
while(!satisfied.empty()){
ModSpec mod = satisfied.back();
m_sorted_mods.push_back(mod);
satisfied.pop_back();
for(std::list<ModSpec>::iterator it = unsatisfied.begin();
it != unsatisfied.end(); ){
ModSpec& mod2 = *it;
mod2.unsatisfied_depends.erase(mod.name);
if(mod2.unsatisfied_depends.empty()){
satisfied.push_back(mod2);
it = unsatisfied.erase(it);
}
else{
++it;
}
}
}
// Step 4: write back list of unsatisfied mods
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
}

@ -30,6 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <exception>
#include <list>
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
class ModError : public std::exception
{
public:
@ -53,23 +55,32 @@ struct ModSpec
std::string path;
//if normal mod:
std::set<std::string> depends;
std::set<std::string> optdepends;
std::set<std::string> unsatisfied_depends;
bool part_of_modpack;
bool is_modpack;
// if modpack:
std::map<std::string,ModSpec> modpack_content;
ModSpec(const std::string name_="", const std::string path_="",
const std::set<std::string> depends_=std::set<std::string>()):
ModSpec(const std::string name_="", const std::string path_=""):
name(name_),
path(path_),
depends(depends_),
unsatisfied_depends(depends_),
depends(),
optdepends(),
unsatisfied_depends(),
part_of_modpack(false),
is_modpack(false),
modpack_content()
{}
};
std::map<std::string,ModSpec> getModsInPath(std::string path);
// Retrieves depends, optdepends, is_modpack and modpack_content
void parseModContents(ModSpec &mod);
std::map<std::string,ModSpec> getModsInPath(std::string path, bool part_of_modpack = false);
// If failed, returned modspec has name==""
ModSpec findCommonMod(const std::string &modname);
// expands modpack contents, but does not replace them.
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
@ -77,10 +88,6 @@ std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mod
// replaces modpack Modspecs with their content
std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
// removes Mods mentioned in exclude_mod_names
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
std::set<std::string> exclude_mod_names);
// a ModConfiguration is a subset of installed mods, expected to have
// all dependencies fullfilled, so it can be used as a list of mods to
// load when the game starts.
@ -89,26 +96,13 @@ class ModConfiguration
public:
ModConfiguration():
m_unsatisfied_mods(),
m_sorted_mods()
m_sorted_mods(),
m_name_conflicts()
{}
ModConfiguration(std::string worldpath);
// adds all mods in the given path. used for games, modpacks
// and world-specific mods (worldmods-folders)
void addModsInPath(std::string path)
{
addMods(flattenMods(getModsInPath(path)));
}
// adds all mods in the given path whose name does not appear
// in the exclude_mods set.
void addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods);
// adds all mods in the set.
void addMods(std::vector<ModSpec> mods);
// checks if all dependencies are fullfilled.
bool isConsistent()
{
@ -120,17 +114,27 @@ public:
return m_sorted_mods;
}
std::list<ModSpec> getUnsatisfiedMods()
std::vector<ModSpec> getUnsatisfiedMods()
{
return m_unsatisfied_mods;
}
private:
// adds all mods in the given path. used for games, modpacks
// and world-specific mods (worldmods-folders)
void addModsInPath(std::string path);
// mods with unmet dependencies. This is a list and not a
// vector because we want easy removal of elements at every
// position.
std::list<ModSpec> m_unsatisfied_mods;
// adds all mods in the set.
void addMods(std::vector<ModSpec> new_mods);
// move mods from m_unsatisfied_mods to m_sorted_mods
// in an order that satisfies dependencies
void resolveDependencies();
// mods with unmet dependencies. Before dependencies are resolved,
// this is where all mods are stored. Afterwards this contains
// only the ones with really unsatisfied dependencies.
std::vector<ModSpec> m_unsatisfied_mods;
// list of mods sorted such that they can be loaded in the
// given order with all dependencies being fullfilled. I.e.,
@ -138,6 +142,16 @@ private:
// appear earlier in the vector.
std::vector<ModSpec> m_sorted_mods;
// set of mod names for which an unresolved name conflict
// exists. A name conflict happens when two or more mods
// at the same level have the same name but different paths.
// Levels (mods in higher levels override mods in lower levels):
// 1. common mod in modpack; 2. common mod;
// 3. game mod in modpack; 4. game mod;
// 5. world mod in modpack; 6. world mod;
// 7. addon mod in modpack; 8. addon mod.
std::set<std::string> m_name_conflicts;
};
#endif

@ -76,8 +76,7 @@ bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
{
ModNameStorer modnamestorer(L, modname);
if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
"0123456789_")){
if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
errorstream<<"Error loading mod \""<<modname
<<"\": modname does not follow naming conventions: "
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;

@ -707,11 +707,11 @@ Server::Server(
ModConfiguration modconf(m_path_world);
m_mods = modconf.getMods();
std::list<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
if(!modconf.isConsistent())
{
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
{
ModSpec mod = *it;
@ -745,7 +745,7 @@ Server::Server(
for(std::vector<ModSpec>::iterator it = m_mods.begin();
it != m_mods.end(); ++it)
load_mod_names.erase((*it).name);
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
load_mod_names.erase((*it).name);
if(!load_mod_names.empty())
@ -2980,12 +2980,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
playersao->setWieldedItem(item);
}
// If item has node placement prediction, always send the above
// node to make sure the client knows what exactly happened
// If item has node placement prediction, always send the
// blocks to make sure the client knows what exactly happened
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
RemoteClient *client = getClient(peer_id);
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
client->SetBlockNotSent(blockpos);
v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
if(blockpos2 != blockpos){
client->SetBlockNotSent(blockpos2);
}
}
} // action == 3
@ -5073,6 +5077,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
getPlayerEffectivePrivs(player->getName()),
isSingleplayer());
/* Clean up old HUD elements from previous sessions */
player->hud.clear();
/* Add object to environment */
m_env->addActiveObject(playersao);

@ -53,7 +53,7 @@ std::vector<ServerListSpec> getLocal()
std::string liststring;
if(fs::PathExists(path))
{
std::ifstream istream(path.c_str(), std::ios::binary);
std::ifstream istream(path.c_str());
if(istream.is_open())
{
std::ostringstream ostream;

@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
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 2 of the License, or
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 General Public License for more details.
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License along
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.
*/

@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
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 2 of the License, or
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 General Public License for more details.
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License along
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.
*/

@ -22,6 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "settings.h"
#include "log.h"
#ifndef SERVER
#include "tile.h" // getImagePath
#endif
#include "util/string.h"
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
@ -94,7 +97,12 @@ SubgameSpec findSubgame(const std::string &id)
std::string game_name = getGameName(game_path);
if(game_name == "")
game_name = id;
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name);
std::string menuicon_path;
#ifndef SERVER
menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
#endif
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
menuicon_path);
}
SubgameSpec findWorldSubgame(const std::string &world_path)

@ -35,17 +35,20 @@ struct SubgameSpec
std::string gamemods_path; //path to mods of the game
std::set<std::string> addon_mods_paths; //paths to addon mods for this game
std::string name;
std::string menuicon_path;
SubgameSpec(const std::string &id_="",
const std::string &path_="",
const std::string &gamemods_path_="",
const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
const std::string &name_=""):
const std::string &name_="",
const std::string &menuicon_path_=""):
id(id_),
path(path_),
gamemods_path(gamemods_path_),
addon_mods_paths(addon_mods_paths_),
name(name_)
name(name_),
menuicon_path(menuicon_path_)
{}
bool isValid() const

@ -77,7 +77,7 @@ static bool replace_ext(std::string &path, const char *ext)
If failed, return "".
*/
static std::string getImagePath(std::string path)
std::string getImagePath(std::string path)
{
// A NULL-ended list of possible image extensions
const char *extensions[] = {

@ -34,6 +34,17 @@ class IGameDef;
tile.{h,cpp}: Texture handling stuff.
*/
/*
Find out the full path of an image by trying different filename
extensions.
If failed, return "".
TODO: Should probably be moved out from here, because things needing
this function do not need anything else from this header
*/
std::string getImagePath(std::string path);
/*
Gets the path to a texture by first checking if the texture exists
in texture_path and if not, using the data path.