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}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
install(FILES ${MINETEST_GAME_SOURCE}/README.txt 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}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game")
endif() endif()
set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build") set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build")
if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE}) if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE})

@ -30,13 +30,17 @@ This game is not finished
Default Controls Default Controls
----------------- -----------------
- WASD: Move - WASD: move
- Space: Jump - Space: jump/climb
- E: Go down - Shift: sneak/go down
- Shift: Sneak - Q: drop item
- Q: Drop item - I: inventory
- I: Open inventory - Mouse: turn/look
- 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. - Settable in the configuration file, see the section below.
Paths 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, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 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., with this program; if not, write to the Free Software Foundation, Inc.,

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

@ -120,6 +120,17 @@ depends.txt:
List of mods that have to be loaded before loading this mod. List of mods that have to be loaded before loading this mod.
A single line contains a single modname. 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: init.lua:
The main Lua script. Running this script should register everything it The main Lua script. Running this script should register everything it
wants to register. Subsequent execution depends on minetest calling the wants to register. Subsequent execution depends on minetest calling the

@ -61,6 +61,9 @@ Run dedicated server
\-\-speedtests \-\-speedtests
Run speed tests Run speed tests
.TP .TP
\-\-videomodes
List available video modes
.TP
\-\-info \-\-info
Print more information to console Print more information to console
.TP .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) #crosshair_color = (255,255,255)
# Cross alpha (opaqueness, between 0 and 255) # Cross alpha (opaqueness, between 0 and 255)
#crosshair_alpha = 255 #crosshair_alpha = 255
# Sensitivity multiplier
#mouse_sensitivity = 0.2
# Sound settings # Sound settings
#enable_sound = true #enable_sound = true
#sound_volume = 0.7 #sound_volume = 0.7

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock.h" #include "mapblock.h"
#include "settings.h" #include "settings.h"
#include "profiler.h" #include "profiler.h"
#include "gettext.h"
#include "log.h" #include "log.h"
#include "nodemetadata.h" #include "nodemetadata.h"
#include "nodedef.h" #include "nodedef.h"
@ -2555,6 +2556,9 @@ void Client::inventoryAction(InventoryAction *a)
Predict some local inventory changes Predict some local inventory changes
*/ */
a->clientApply(this, this); a->clientApply(this, this);
// Remove it
delete a;
} }
ClientActiveObject * Client::getSelectedActiveObject( ClientActiveObject * Client::getSelectedActiveObject(
@ -2801,7 +2805,10 @@ ClientEvent Client::getClientEvent()
return m_client_event_queue.pop_front(); 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; infostream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received); assert(m_itemdef_received);
@ -2836,13 +2843,23 @@ void Client::afterContentReceived()
if(g_settings->getBool("preload_item_visuals")) if(g_settings->getBool("preload_item_visuals"))
{ {
verbosestream<<"Updating item textures and meshes"<<std::endl; 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(); 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 for(std::set<std::string>::const_iterator
i = names.begin(); i != names.end(); ++i){ i = names.begin(); i != names.end(); ++i){
// Asking for these caches the result // Asking for these caches the result
m_itemdef->getInventoryTexture(*i, this); m_itemdef->getInventoryTexture(*i, this);
m_itemdef->getWieldMesh(*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 // Start mesh update thread after setting up content definitions

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

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

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

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

@ -395,11 +395,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
/* /*
Draws a screen with a single text on it. Draws a screen with a single text on it.
Text will be removed when the screen is drawn the next time. 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 **/ /*gui::IGUIStaticText **/
void draw_load_screen(const std::wstring &text, 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(); v2u32 screensize = driver->getScreenSize();
const wchar_t *loadingtext = text.c_str(); const wchar_t *loadingtext = text.c_str();
core::vector2d<u32> textsize_u = font->getDimension(loadingtext); core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
@ -411,7 +414,30 @@ void draw_load_screen(const std::wstring &text,
loadingtext, textrect, false, false); loadingtext, textrect, false, false);
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); 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(); guienv->drawAll();
driver->endScene(); 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( void the_game(
bool &kill, bool &kill,
bool random_input, bool random_input,
@ -821,7 +908,11 @@ void the_game(
Draw "Loading" screen 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 // Create texture source
IWritableTextureSource *tsrc = createTextureSource(device); IWritableTextureSource *tsrc = createTextureSource(device);
@ -878,7 +969,9 @@ void the_game(
*/ */
if(address == ""){ 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; infostream<<"Creating server"<<std::endl;
server = new Server(map_dir, configpath, gamespec, server = new Server(map_dir, configpath, gamespec,
simple_singleplayer_mode); simple_singleplayer_mode);
@ -891,7 +984,11 @@ void the_game(
Create client 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; infostream<<"Creating client"<<std::endl;
MapDrawControl draw_control; MapDrawControl draw_control;
@ -901,8 +998,12 @@ void the_game(
// Client acts as our GameDef // Client acts as our GameDef
IGameDef *gamedef = &client; 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); Address connect_address(0,0,0,0, port);
try{ try{
if(address == "") if(address == "")
@ -934,15 +1035,26 @@ void the_game(
bool could_connect = false; bool could_connect = false;
bool connect_aborted = false; bool connect_aborted = false;
try{ try{
float frametime = 0.033;
float time_counter = 0.0; float time_counter = 0.0;
input->clear(); 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()) 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 // Update client and server
client.step(frametime); client.step(dtime);
if(server != NULL) if(server != NULL)
server->step(frametime); server->step(dtime);
// End condition // End condition
if(client.connectedAndInitialized()){ if(client.connectedAndInitialized()){
@ -963,15 +1075,37 @@ void the_game(
} }
// Display status // Display status
std::wostringstream ss; {
ss<<L"Connecting to server... (press Escape to cancel)\n"; wchar_t* text = wgettext("Connecting to server...");
std::wstring animation = L"/-\\|"; draw_load_screen(text, device, font, dtime, 100);
ss<<animation[(int)(time_counter/0.2)%4]; delete[] text;
draw_load_screen(ss.str(), driver, font); }
// Delay a bit // On some computers framerate doesn't seem to be
sleep_ms(1000*frametime); // automatically limited
time_counter += frametime; 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) catch(con::PeerNotFoundException &e)
@ -995,15 +1129,26 @@ void the_game(
bool got_content = false; bool got_content = false;
bool content_aborted = false; bool content_aborted = false;
{ {
float frametime = 0.033;
float time_counter = 0.0; float time_counter = 0.0;
input->clear(); 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()) 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 // Update client and server
client.step(frametime); client.step(dtime);
if(server != NULL) if(server != NULL)
server->step(frametime); server->step(dtime);
// End condition // End condition
if(client.texturesReceived() && if(client.texturesReceived() &&
@ -1025,21 +1170,52 @@ void the_game(
} }
// Display status // Display status
std::wostringstream ss; std::ostringstream ss;
ss<<L"Waiting content... (press Escape to cancel)\n"; int progress=0;
if (!client.itemdefReceived())
ss<<(client.itemdefReceived()?L"[X]":L"[ ]"); {
ss<<L" Item definitions\n"; ss << "Item definitions...";
ss<<(client.nodedefReceived()?L"[X]":L"[ ]"); progress = 0;
ss<<L" Node definitions\n"; }
ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] "; else if (!client.nodedefReceived())
ss<<L" Media\n"; {
ss << "Node definitions...";
draw_load_screen(ss.str(), driver, font); 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;
// Delay a bit // On some computers framerate doesn't seem to be
sleep_ms(1000*frametime); // automatically limited
time_counter += frametime; 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;
} }
} }
@ -1056,7 +1232,7 @@ void the_game(
After all content has been received: After all content has been received:
Update cached textures, meshes and materials Update cached textures, meshes and materials
*/ */
client.afterContentReceived(); client.afterContentReceived(device,font);
/* /*
Create the camera node Create the camera node
@ -1885,7 +2061,8 @@ void the_game(
if(input->isKeyDown(irr::KEY_RIGHT)) if(input->isKeyDown(irr::KEY_RIGHT))
dx += dtime * keyspeed;*/ 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_yaw -= dx*d;
camera_pitch += dy*d; camera_pitch += dy*d;
if(camera_pitch < -89.5) camera_pitch = -89.5; if(camera_pitch < -89.5) camera_pitch = -89.5;
@ -2198,17 +2375,15 @@ void the_game(
- Can it point to liquids? - Can it point to liquids?
*/ */
ItemStack playeritem; ItemStack playeritem;
bool playeritem_usable = false;
bool playeritem_liquids_pointable = false;
{ {
InventoryList *mlist = local_inventory.getList("main"); InventoryList *mlist = local_inventory.getList("main");
if(mlist != NULL) if(mlist != NULL)
{ {
playeritem = mlist->getItem(client.getPlayerItem()); 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 = ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef); playeritem.getToolCapabilities(itemdef);
@ -2267,7 +2442,7 @@ void the_game(
// input // input
&client, player_position, camera_direction, &client, player_position, camera_direction,
camera_position, shootline, d, camera_position, shootline, d,
playeritem_liquids_pointable, !ldown_for_dig, playeritem_def.liquids_pointable, !ldown_for_dig,
// output // output
hilightboxes, hilightboxes,
selected_object); selected_object);
@ -2327,7 +2502,7 @@ void the_game(
else else
repeat_rightclick_timer = 0; repeat_rightclick_timer = 0;
if(playeritem_usable && input->getLeftState()) if(playeritem_def.usable && input->getLeftState())
{ {
if(input->getLeftClicked()) if(input->getLeftClicked())
client.interact(4, pointed); client.interact(4, pointed);
@ -2534,46 +2709,13 @@ void the_game(
// If the wielded item has node placement prediction, // If the wielded item has node placement prediction,
// make that happen // make that happen
const ItemDefinition &def = nodePlacementPrediction(client,
playeritem.getDefinition(itemdef); playeritem_def,
if(def.node_placement_prediction != "" nodepos, neighbourpos);
&& !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);
// Read the sound // 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(); clouds->drop();
if (gui_chat_console) if (gui_chat_console)
gui_chat_console->drop(); gui_chat_console->drop();
if (sky)
sky->drop();
clear_particles(); clear_particles();
/* /*
@ -3201,7 +3345,9 @@ void the_game(
*/ */
{ {
/*gui::IGUIStaticText *gui_shuttingdowntext = */ /*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)); /*driver->beginScene(true, true, video::SColor(255,0,0,0));
guienv->drawAll(); guienv->drawAll();
driver->endScene(); driver->endScene();

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

@ -407,14 +407,26 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event)
delete[] text; delete[] text;
menu->drop(); menu->drop();
ModConfiguration modconf(m_wspec.path); try
if(!modconf.isConsistent())
{ {
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 = GUIMessageMenu *menu =
new GUIMessageMenu(Environment, Parent, -1, m_menumgr, new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
text ); text );
delete[] text;
menu->drop(); menu->drop();
} }

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

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

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

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

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

@ -226,17 +226,11 @@ class CItemDefManager: public IWritableItemDefManager
public: public:
CItemDefManager() 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 #ifndef SERVER
m_main_thread = get_current_thread_id(); m_main_thread = get_current_thread_id();
m_driver = NULL; m_driver = NULL;
#endif #endif
clear(); clear();
} }
virtual ~CItemDefManager() virtual ~CItemDefManager()
@ -260,6 +254,12 @@ public:
} }
m_driver = NULL; m_driver = NULL;
#endif #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 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 "subgame.h"
#include "quicktune.h" #include "quicktune.h"
#include "serverlist.h" #include "serverlist.h"
#include "sound.h"
#include "sound_openal.h"
/* /*
Settings. Settings.
@ -88,6 +90,10 @@ Settings *g_settings = &main_settings;
Profiler main_profiler; Profiler main_profiler;
Profiler *g_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 Debug streams
*/ */
@ -196,7 +202,49 @@ u32 getTime(TimePrecision prec) {
return 0; return 0;
return g_timegetter->getTime(prec); 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 #endif
class StderrLogOutput: public ILogOutput class StderrLogOutput: public ILogOutput
@ -612,122 +660,181 @@ private:
bool rightreleased; bool rightreleased;
}; };
//Draw the tiled menu background struct MenuTextures
void drawMenuBackground(video::IVideoDriver* driver) { {
core::dimension2d<u32> screensize = driver->getScreenSize(); 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"); MenuTextures():
if (path[0]) { global_textures(false),
static const video::ITexture *bgtexture = background(NULL),
driver->getTexture(path.c_str()); overlay(NULL),
header(NULL),
footer(NULL)
{}
if (bgtexture) { static video::ITexture* getMenuTexture(const std::string &tname,
s32 scaledsize = 128; video::IVideoDriver* driver, const SubgameSpec *spec)
{
// The important difference between destsize and screensize is if(spec){
// that destsize is rounded to whole scaled pixels. std::string path;
// These formulas use component-wise multiplication and division of v2u32. // eg. minetest_menu_background.png (for texture packs)
v2u32 texturesize = bgtexture->getSize(); std::string pack_tname = spec->id + "_menu_" + tname + ".png";
v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1); path = getTexturePath(pack_tname);
v2u32 destsize = scaledsize * sourcesize / texturesize; if(path != "")
return driver->getTexture(path.c_str());
// Default texture wrapping mode in Irrlicht is ETC_REPEAT. // eg. games/minetest_game/menu/background.png
driver->draw2DImage(bgtexture, path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
core::rect<s32>(0, 0, destsize.X, destsize.Y), if(path != "")
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), return driver->getTexture(path.c_str());
NULL, NULL, true); } 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, const MenuTextures &menutextures)
void drawMenuFooter(video::IVideoDriver* driver, bool clouds) { {
core::dimension2d<u32> screensize = driver->getScreenSize(); core::dimension2d<u32> screensize = driver->getScreenSize();
std::string path = getTexturePath(clouds ? video::ITexture *texture = menutextures.footer;
"menufooter_clouds.png" : "menufooter.png");
if (path[0]) {
static const video::ITexture *footertexture =
driver->getTexture(path.c_str());
if (footertexture) { /* If no texture, draw nothing */
f32 mult = (((f32)screensize.Width)) / if(!texture)
((f32)footertexture->getOriginalSize().Width); return;
v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult, f32 mult = (((f32)screensize.Width)) /
((f32)footertexture->getOriginalSize().Height) * mult); ((f32)texture->getOriginalSize().Width);
// Don't draw the footer if there isn't enough room v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
s32 free_space = (((s32)screensize.Height)-320)/2; ((f32)texture->getOriginalSize().Height) * mult);
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(footertexture, rect, // Don't draw the footer if there isn't enough room
core::rect<s32>(core::position2d<s32>(0,0), s32 free_space = (((s32)screensize.Height)-320)/2;
core::dimension2di(footertexture->getSize())), if (free_space > footersize.Y) {
NULL, NULL, true); 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 static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
void drawMenuHeader(video::IVideoDriver* driver) { {
core::dimension2d<u32> screensize = driver->getScreenSize(); for(size_t i=0; i<menudata.games.size(); i++){
if(menudata.games[i].id == menudata.selected_game){
std::string path = getTexturePath("menuheader.png"); return &menudata.games[i];
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);
}
} }
} }
return NULL;
} }
// Draw the Splash over the clouds and under the main menu #endif // !SERVER
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
// These are defined global so that they're not optimized too much. // These are defined global so that they're not optimized too much.
// Can't change them to volatile. // 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, allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
_("Set gameid (\"--gameid list\" prints available ones)")))); _("Set gameid (\"--gameid list\" prints available ones)"))));
#ifndef SERVER #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, allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
_("Run speed tests")))); _("Run speed tests"))));
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING, allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
@ -999,7 +1108,7 @@ int main(int argc, char *argv[])
print_worldspecs(worldspecs, dstream); print_worldspecs(worldspecs, dstream);
return 0; return 0;
} }
// Print startup message // Print startup message
infostream<<PROJECT_NAME<< infostream<<PROJECT_NAME<<
" "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
@ -1379,11 +1488,64 @@ int main(int argc, char *argv[])
} }
/* /*
Create device and exit if creation failed List video modes if requested
*/ */
MyEventReceiver receiver; 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; IrrlichtDevice *device;
SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
@ -1426,6 +1588,7 @@ int main(int argc, char *argv[])
{ {
dstream<<"Running speed tests"<<std::endl; dstream<<"Running speed tests"<<std::endl;
SpeedTests(); SpeedTests();
device->drop();
return 0; return 0;
} }
@ -1477,6 +1640,19 @@ int main(int argc, char *argv[])
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49)); skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
#endif #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 GUI stuff
*/ */
@ -1558,6 +1734,10 @@ int main(int argc, char *argv[])
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab"); menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
if(g_settings->exists("selected_serverlist")) if(g_settings->exists("selected_serverlist"))
menudata.selected_serverlist = g_settings->getS32("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.address = narrow_to_wide(address);
menudata.name = narrow_to_wide(playername); menudata.name = narrow_to_wide(playername);
menudata.port = narrow_to_wide(itos(port)); menudata.port = narrow_to_wide(itos(port));
@ -1611,6 +1791,17 @@ int main(int argc, char *argv[])
} }
// Copy worldspecs to menu // Copy worldspecs to menu
menudata.worlds = worldspecs; 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) if(skip_main_menu == false)
{ {
@ -1623,7 +1814,7 @@ int main(int argc, char *argv[])
break; break;
driver->beginScene(true, true, driver->beginScene(true, true,
video::SColor(255,128,128,128)); video::SColor(255,128,128,128));
drawMenuBackground(driver); drawMenuBackground(driver, menutextures);
guienv->drawAll(); guienv->drawAll();
driver->endScene(); driver->endScene();
// On some computers framerate doesn't seem to be // On some computers framerate doesn't seem to be
@ -1637,22 +1828,6 @@ int main(int argc, char *argv[])
&g_menumgr, &menudata, g_gamecallback); &g_menumgr, &menudata, g_gamecallback);
menu->allowFocusRemoval(true); 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"") if(error_message != L"")
{ {
verbosestream<<"error_message = " verbosestream<<"error_message = "
@ -1668,6 +1843,16 @@ int main(int argc, char *argv[])
// Time is in milliseconds, for clouds // Time is in milliseconds, for clouds
u32 lasttime = device->getTimer()->getTime(); 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; infostream<<"Created main menu"<<std::endl;
while(device->run() && kill == false) while(device->run() && kill == false)
@ -1675,8 +1860,25 @@ int main(int argc, char *argv[])
if(menu->getStatus() == true) if(menu->getStatus() == true)
break; 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 // Time calc for the clouds
f32 dtime; // in seconds f32 dtime=0; // in seconds
if (cloud_menu_background) { if (cloud_menu_background) {
u32 time = device->getTimer()->getTime(); u32 time = device->getTimer()->getTime();
if(time > lasttime) if(time > lasttime)
@ -1691,15 +1893,16 @@ int main(int argc, char *argv[])
if (cloud_menu_background) { if (cloud_menu_background) {
// *3 otherwise the clouds would move very slowly // *3 otherwise the clouds would move very slowly
clouds->step(dtime*3); g_menuclouds->step(dtime*3);
clouds->render(); g_menuclouds->render();
smgr->drawAll(); g_menucloudsmgr->drawAll();
drawMenuSplash(driver); drawMenuOverlay(driver, menutextures);
drawMenuFooter(driver, true); drawMenuHeader(driver, menutextures);
drawMenuHeader(driver); drawMenuFooter(driver, menutextures);
} else { } else {
drawMenuBackground(driver); drawMenuBackground(driver, menutextures);
drawMenuFooter(driver, false); drawMenuHeader(driver, menutextures);
drawMenuFooter(driver, menutextures);
} }
guienv->drawAll(); guienv->drawAll();
@ -1731,14 +1934,15 @@ int main(int argc, char *argv[])
sleep_ms(25); sleep_ms(25);
} }
} }
sound->stopSound(handle);
if(sound != &dummySoundManager){
delete sound;
sound = NULL;
}
infostream<<"Dropping main menu"<<std::endl; infostream<<"Dropping main menu"<<std::endl;
menu->drop(); menu->drop();
if (cloud_menu_background) {
clouds->drop();
smgr->clear();
}
} }
playername = wide_to_narrow(menudata.name); playername = wide_to_narrow(menudata.name);
@ -1755,6 +1959,7 @@ int main(int argc, char *argv[])
// Save settings // Save settings
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab); g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
g_settings->setS32("selected_serverlist", menudata.selected_serverlist); 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("new_style_leaves", itos(menudata.fancy_trees));
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting)); g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d)); g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
@ -1832,6 +2037,7 @@ int main(int argc, char *argv[])
continue; continue;
} }
g_settings->set("selected_world_path", path); g_settings->set("selected_world_path", path);
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
continue; continue;
} }
@ -1897,6 +2103,7 @@ int main(int argc, char *argv[])
gamespec, gamespec,
simple_singleplayer_mode simple_singleplayer_mode
); );
smgr->clear();
} //try } //try
catch(con::PeerNotFoundException &e) catch(con::PeerNotFoundException &e)
@ -1927,6 +2134,10 @@ int main(int argc, char *argv[])
} }
} // Menu-game loop } // Menu-game loop
g_menuclouds->drop();
g_menucloudsmgr->drop();
delete input; delete input;
/* /*
@ -1934,6 +2145,8 @@ int main(int argc, char *argv[])
*/ */
device->drop(); device->drop();
delete font;
#endif // !SERVER #endif // !SERVER
// Update configuration file // Update configuration file

@ -28,6 +28,14 @@ extern Settings *g_settings;
class Profiler; class Profiler;
extern Profiler *g_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 // Debug streams
#include <fstream> #include <fstream>

@ -1338,12 +1338,20 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices, const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices) const u16 *indices, u32 numIndices)
{ {
if(numIndices > 65535)
{
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
PreMeshBuffer *p = NULL; PreMeshBuffer *p = NULL;
for(u32 i=0; i<prebuffers.size(); i++) for(u32 i=0; i<prebuffers.size(); i++)
{ {
PreMeshBuffer &pp = prebuffers[i]; PreMeshBuffer &pp = prebuffers[i];
if(pp.tile != tile) if(pp.tile != tile)
continue; continue;
if(pp.indices.size() + numIndices > 65535)
continue;
p = &pp; p = &pp;
break; break;
@ -1361,11 +1369,6 @@ void MeshCollector::append(const TileSpec &tile,
for(u32 i=0; i<numIndices; i++) for(u32 i=0; i<numIndices; i++)
{ {
u32 j = indices[i] + vertex_count; 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); p->indices.push_back(j);
} }
for(u32 i=0; i<numVertices; i++) for(u32 i=0; i<numVertices; i++)

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

@ -24,9 +24,74 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "subgame.h" #include "subgame.h"
#include "settings.h" #include "settings.h"
#include "strfnd.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::map<std::string, ModSpec> result;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path); std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
for(u32 j=0; j<dirlist.size(); j++){ for(u32 j=0; j<dirlist.size(); j++){
@ -39,38 +104,34 @@ std::map<std::string, ModSpec> getModsInPath(std::string path)
continue; continue;
std::string modpath = path + DIR_DELIM + modname; std::string modpath = path + DIR_DELIM + modname;
// Handle modpacks (defined by containing modpack.txt) ModSpec spec(modname, modpath);
std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(), spec.part_of_modpack = part_of_modpack;
std::ios_base::binary); parseModContents(spec);
if(modpack_is.good()) //a modpack, recursively get the mods in it result.insert(std::make_pair(modname, spec));
{
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));
}
} }
return result; 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> flattenModTree(std::map<std::string, ModSpec> mods)
{ {
std::map<std::string, ModSpec> result; std::map<std::string, ModSpec> result;
@ -109,109 +170,18 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
} }
else //not a modpack else //not a modpack
{ {
// infostream << "inserting mod " << mod.name << std::endl;
result.push_back(mod); result.push_back(mod);
} }
} }
return result; 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) ModConfiguration::ModConfiguration(std::string worldpath)
{ {
SubgameSpec gamespec = findWorldSubgame(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; std::vector<std::string> inexistent_common_mods;
Settings gameconf; Settings gameconf;
if(getGameConfig(gamespec.path, gameconf)){ if(getGameConfig(gamespec.path, gameconf)){
@ -225,7 +195,7 @@ ModConfiguration::ModConfiguration(std::string worldpath)
if(spec.name.empty()) if(spec.name.empty())
inexistent_common_mods.push_back(modname); inexistent_common_mods.push_back(modname);
else 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."; s += " could not be found.";
throw ModError(s); throw ModError(s);
} }
addMods(flattenMods(common_mods));
// Add all world mods and all game mods // Add all game mods and all world mods
addModsInPath(worldpath + DIR_DELIM + "worldmods");
addModsInPath(gamespec.gamemods_path); addModsInPath(gamespec.gamemods_path);
addModsInPath(worldpath + DIR_DELIM + "worldmods");
// check world.mt file for mods explicitely declared to be // check world.mt file for mods explicitely declared to be
// loaded or not by a load_mod_<modname> = ... line. // 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(); // Collect all mods in gamespec.addon_mods_paths,
i != gamespec.addon_mods_paths.end(); ++i) // excluding those in the set exclude_mod_names
addModsInPathFiltered((*i),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 <exception>
#include <list> #include <list>
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
class ModError : public std::exception class ModError : public std::exception
{ {
public: public:
@ -53,23 +55,32 @@ struct ModSpec
std::string path; std::string path;
//if normal mod: //if normal mod:
std::set<std::string> depends; std::set<std::string> depends;
std::set<std::string> optdepends;
std::set<std::string> unsatisfied_depends; std::set<std::string> unsatisfied_depends;
bool part_of_modpack;
bool is_modpack; bool is_modpack;
// if modpack: // if modpack:
std::map<std::string,ModSpec> modpack_content; std::map<std::string,ModSpec> modpack_content;
ModSpec(const std::string name_="", const std::string path_="", ModSpec(const std::string name_="", const std::string path_=""):
const std::set<std::string> depends_=std::set<std::string>()):
name(name_), name(name_),
path(path_), path(path_),
depends(depends_), depends(),
unsatisfied_depends(depends_), optdepends(),
is_modpack(false), unsatisfied_depends(),
modpack_content() 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. // expands modpack contents, but does not replace them.
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods); 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 // replaces modpack Modspecs with their content
std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods); 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 // 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 // all dependencies fullfilled, so it can be used as a list of mods to
// load when the game starts. // load when the game starts.
@ -89,26 +96,13 @@ class ModConfiguration
public: public:
ModConfiguration(): ModConfiguration():
m_unsatisfied_mods(), m_unsatisfied_mods(),
m_sorted_mods() m_sorted_mods(),
m_name_conflicts()
{} {}
ModConfiguration(std::string worldpath); 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. // checks if all dependencies are fullfilled.
bool isConsistent() bool isConsistent()
{ {
@ -120,17 +114,27 @@ public:
return m_sorted_mods; return m_sorted_mods;
} }
std::list<ModSpec> getUnsatisfiedMods() std::vector<ModSpec> getUnsatisfiedMods()
{ {
return m_unsatisfied_mods; return m_unsatisfied_mods;
} }
private: 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 // adds all mods in the set.
// vector because we want easy removal of elements at every void addMods(std::vector<ModSpec> new_mods);
// position.
std::list<ModSpec> m_unsatisfied_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 // list of mods sorted such that they can be loaded in the
// given order with all dependencies being fullfilled. I.e., // given order with all dependencies being fullfilled. I.e.,
@ -138,6 +142,16 @@ private:
// appear earlier in the vector. // appear earlier in the vector.
std::vector<ModSpec> m_sorted_mods; 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 #endif

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

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

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

@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013 Kahrl <kahrl@gmx.net> Copyright (C) 2013 Kahrl <kahrl@gmx.net>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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., with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 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> Copyright (C) 2013 Kahrl <kahrl@gmx.net>
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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., with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 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 "filesys.h"
#include "settings.h" #include "settings.h"
#include "log.h" #include "log.h"
#ifndef SERVER
#include "tile.h" // getImagePath
#endif
#include "util/string.h" #include "util/string.h"
bool getGameMinetestConfig(const std::string &game_path, Settings &conf) 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); std::string game_name = getGameName(game_path);
if(game_name == "") if(game_name == "")
game_name = id; 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) SubgameSpec findWorldSubgame(const std::string &world_path)

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

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

@ -34,6 +34,17 @@ class IGameDef;
tile.{h,cpp}: Texture handling stuff. 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 Gets the path to a texture by first checking if the texture exists
in texture_path and if not, using the data path. in texture_path and if not, using the data path.