Compare commits

..

37 Commits

Author SHA1 Message Date
Perttu Ahola
9696ed31a4 Version 0.4.3 2012-09-05 01:31:06 +03:00
Matthew I
c8895dd43b Prevent world creation if the world already exists 2012-09-05 01:17:44 +03:00
Matthew I
5dd1d354f8 Enforce stricter world names using a blacklist
Blacklisted characters are: / \
2012-09-05 01:17:28 +03:00
Perttu Ahola
a0da6bcf43 Add aux1_descends to key change menu 2012-09-05 00:55:19 +03:00
Perttu Ahola
1b63f8d6b9 Version 0.4.3-rc0; increase PROTOCOL_VERSION due to backwards-incompatible InventoryList width field 2012-09-05 00:39:43 +03:00
Perttu Ahola
60932d2bd7 Remove "Unknown inventory identifier" deserialization errors, because they can break forward compatibility 2012-09-04 23:02:56 +03:00
Perttu Ahola
ee2d9d973a Reorganize ClientMap rendering code for a bit more performance
- Don't select blocks for drawing in every frame
- Sort meshbuffers by material before drawing
2012-09-04 22:41:03 +03:00
Perttu Ahola
0e6f7a21c6 Fix shift-descend to ladders from a floor 2012-09-03 23:31:51 +03:00
Perttu Ahola
cc47ff7c4c Fix moving stuff into a mismatched stack in a "infinite" inventory 2012-09-02 23:01:42 +03:00
Perttu Ahola
e1a495ee30 Make inventory GUI do sane things when server-side inventory acts unusually 2012-09-02 22:51:38 +03:00
Perttu Ahola
6495007924 Fix bug in moving a larger sized stack in inventory on top of another
- You have 2 stacks: a with x items and b with y<x items
- Take a with your mouse and place it on b
- Whole stack a moves to b
- (y * 2 - x) items move from b to a
- (x - y) items are left to the mouse cursor
2012-09-02 20:24:45 +03:00
Perttu Ahola
d53a6cd851 Add overridable function for handling dropped items from nodes 2012-09-02 19:38:44 +03:00
Perttu Ahola
b6c12d2aa3 Fix github issue #224 2012-09-01 18:32:15 +03:00
Thomas Lauro
7a14171be4 Fix issue with openal mac os x compile 2012-09-01 18:19:59 +03:00
Perttu Ahola
5194505407 Fix GUIKeyChangeMenu so that '/' can be inserted on a finnish keyboard 2012-09-01 18:03:50 +03:00
Thomas Lauro
3e7957512b A bunch of GUIKeyChangeMenu fixes
- Display message when associating a key already in use
- Fix issue for not beeing able to assign space to an action
- Cleaning up
- Make it build (by celeron55)
2012-09-01 18:03:40 +03:00
Perttu Ahola
c0bd96d9b3 Attempt to fix flying gravel and dirt 2012-09-01 13:41:15 +03:00
Sergey Gilfanov
0f7728f84d Smooth transition to the desert biome. 2012-09-01 13:22:07 +03:00
Perttu Ahola
c129b3852b Fix the bouncy node related stuff a bit 2012-09-01 13:21:52 +03:00
Perttu Ahola
3b43c69df4 Add bouncy node group 2012-09-01 12:58:37 +03:00
Perttu Ahola
b17d3e7ad7 Add disable_jump and fall_damage_add_percent node groups 2012-09-01 12:07:27 +03:00
Ilya Zhuravlev
6a16075912 Add InventoryList width property & allow custom crafting grids. 2012-09-01 10:01:41 +03:00
sfan5
43ebec2be1 Make shift the default descent control on ladders and when flying
"aux1_descends" setting switches to the old descend control.
2012-09-01 09:48:07 +03:00
Perttu Ahola
f60227f112 Don't crash in "unexpected multibyte character"; just print it in log. Github #222 2012-09-01 09:23:39 +03:00
Perttu Ahola
f3630e95d6 Use proper alpha blit in some more texture modifiers 2012-08-31 18:06:47 +03:00
Perttu Ahola
8887105ffd Properly initialize baseimg for texture modifier [combine:WxH:X,Y=filename:X,Y=filename2 2012-08-31 17:58:55 +03:00
Matthew I
1d7408a7b8 Fix black display inside opaque water
Opaque water's solidness was being set to 2, like a normal node.
When you swim, it is treated like a solid block, and the display
goes black.  Setting it to 1 like transparent water allows you to
see.

It looks somewhat awkward when you swim, look up, and see an opaque
wall of water (the surface), but there isn't much that can be done
about it.  If you made the water transparent so it looked good,
it would defeat the purpose :) .
2012-08-31 17:50:12 +03:00
Perttu Ahola
0b1ab41a95 Fix github issue #213: Dropping unknown items crashes the game
The items will now just disappear when dropped.
2012-08-19 10:55:04 +03:00
Perttu Ahola
b717b6505b Fix crash when furnace is full (minimal game) 2012-08-12 17:11:23 +03:00
Matthew I
b29d609b0b Move chat commands to Lua and remove servercommand.{cpp,h}
Commands moved:
 /me
 /status
 /time
 /shutdown
 /ban
 /clearobjects
2012-08-12 16:45:58 +03:00
Matthew I
8a3c777c40 Add ban management routines to Lua API
Those functions are:
 * minetest.get_ban_list()
 * minetest.get_ban_description(ip_or_name)
 * minetest.ban_player(name)
 * minetest.unban_player_or_ip(ip_or_name)
2012-08-12 16:45:58 +03:00
Matthew I
548da26ddc Extend Lua API in order to implement chat commands
minetest.get_server_status()
minetest.request_shutdown()
EnvRef:clear_objects()
2012-08-12 16:44:55 +03:00
Matthew I
8e0ac70c4d Remove trailing comma after player list in server status string 2012-08-12 16:44:54 +03:00
Matthew I
1ed559bd24 Allow digging of unknown nodes
This allows the removal of nodes with unknown types.
get_item_callback() (C++) would fail if a node has an unknown type.  Now it
will try using the callback from minetest.nodedef_default in this case.
Also, minetest.node_dig() (Lua) was altered to always allow digging when
the node definition is empty (i.e. unknown node).
2012-08-12 16:04:18 +03:00
Perttu Ahola
e3b831e975 Fix wrong amount of nodes being dropped from inventory 2012-08-12 15:49:23 +03:00
darkrose
d05e3adbc7 fix node timers so on_timer gets the correct position 2012-08-12 15:28:31 +03:00
Perttu Ahola
a9a475ad50 Remove unwanted ! from ifs in inventory record-for-rollback code 2012-08-12 11:20:36 +03:00
44 changed files with 1197 additions and 1083 deletions

View File

@@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
# Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 2-rc1)
set(VERSION_PATCH 3)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()

View File

@@ -20,18 +20,13 @@ minetest.register_on_chat_message(function(name, message)
end
local cmd_def = minetest.chatcommands[cmd]
if cmd_def then
if not cmd_def.func then
-- This is a C++ command
return false
local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
if has_privs then
cmd_def.func(name, param)
else
local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
if has_privs then
cmd_def.func(name, param)
else
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
end
return true -- handled chat message
minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
end
return true -- handled chat message
end
return false
end)
@@ -39,17 +34,15 @@ end)
--
-- Chat commands
--
minetest.register_chatcommand("me", {
params = "<action>",
description = "chat action (eg. /me orders a pizza)",
privs = {shout=true},
func = function(name, param)
minetest.chat_send_all("* " .. name .. " " .. param)
end,
})
-- Register C++ commands without functions
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)", privs = {shout=true}})
minetest.register_chatcommand("status", {description = "print server status line"})
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
-- Register other commands
minetest.register_chatcommand("help", {
privs = {},
params = "(nothing)/all/privs/<cmd>",
@@ -575,3 +568,92 @@ minetest.register_chatcommand("rollback", {
end,
})
minetest.register_chatcommand("status", {
params = "",
description = "print server status line",
privs = {},
func = function(name, param)
minetest.chat_send_player(name, minetest.get_server_status())
end,
})
minetest.register_chatcommand("time", {
params = "<0...24000>",
description = "set time of day",
privs = {settime=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Missing parameter")
return
end
local newtime = tonumber(param)
if newtime == nil then
minetest.chat_send_player(name, "Invalid time")
else
minetest.env:set_timeofday((newtime % 24000) / 24000)
minetest.chat_send_player(name, "Time of day changed.")
minetest.log("action", name .. " sets time " .. newtime)
end
end,
})
minetest.register_chatcommand("shutdown", {
params = "",
description = "shutdown server",
privs = {server=true},
func = function(name, param)
minetest.log("action", name .. " shuts down server")
minetest.request_shutdown()
minetest.chat_send_all("*** Server shutting down (operator request).")
end,
})
minetest.register_chatcommand("ban", {
params = "<name>",
description = "ban IP of player",
privs = {ban=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list())
return
end
if not minetest.env:get_player_by_name(param) then
minetest.chat_send_player(name, "No such player")
return
end
if not minetest.ban_player(param) then
minetest.chat_send_player(name, "Failed to ban player")
else
local desc = minetest.get_ban_description(param)
minetest.chat_send_player(name, "Banned " .. desc .. ".")
minetest.log("action", name .. " bans " .. desc .. ".")
end
end,
})
minetest.register_chatcommand("unban", {
params = "<name/ip>",
description = "remove IP ban",
privs = {ban=true},
func = function(name, param)
if not minetest.unban_player_or_ip(param) then
minetest.chat_send_player(name, "Failed to unban player/IP")
else
minetest.chat_send_player(name, "Unbanned " .. param)
minetest.log("action", name .. " unbans " .. param)
end
end,
})
minetest.register_chatcommand("clearobjects", {
params = "",
description = "clear all objects in world",
privs = {server=true},
func = function(name, param)
minetest.log("action", name .. " clears all objects")
minetest.chat_send_all("Clearing all objects. This may take long. You may experience a timeout. (by " .. name .. ")")
minetest.env:clear_objects()
minetest.log("action", "object clearing done")
minetest.chat_send_all("*** Cleared all objects.")
end,
})

View File

@@ -227,10 +227,12 @@ function minetest.item_drop(itemstack, dropper, pos)
local v = dropper:get_look_dir()
local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
local obj = minetest.env:add_item(p, itemstack)
v.x = v.x*2
v.y = v.y*2 + 1
v.z = v.z*2
obj:setvelocity(v)
if obj then
v.x = v.x*2
v.y = v.y*2 + 1
v.z = v.z*2
obj:setvelocity(v)
end
else
minetest.env:add_item(pos, itemstack)
end
@@ -258,11 +260,22 @@ function minetest.node_punch(pos, node, puncher)
end
end
function minetest.handle_node_drops(pos, drops, digger)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
end
function minetest.node_dig(pos, node, digger)
minetest.debug("node_dig")
local def = ItemStack({name=node.name}):get_definition()
if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
-- Check if def ~= 0 because we always want to be able to remove unknown nodes
if #def ~= 0 and not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
minetest.debug("not diggable")
minetest.log("info", digger:get_player_name() .. " tried to dig "
.. node.name .. " which is not diggable "
@@ -281,14 +294,9 @@ function minetest.node_dig(pos, node, digger)
local dp = minetest.get_dig_params(def.groups, tp)
wielded:add_wear(dp.wear)
digger:set_wielded_item(wielded)
-- Add dropped items to object's inventory
if digger:get_inventory() then
local _, dropped_item
for _, dropped_item in ipairs(drops) do
digger:get_inventory():add_item("main", dropped_item)
end
end
-- Handle drops
minetest.handle_node_drops(pos, drops, digger)
local oldmetadata = nil
if def.after_dig_node then

View File

@@ -453,6 +453,9 @@ Special groups
- 2: node is removed without tool wear after 0.5 seconds or so
(rail, sign)
- 3: node is removed without tool wear immediately (torch)
- disable_jump: Player (and possibly other things) cannot jump from node
- fall_damage_add_percent: damage speed = speed * (1 + value/100)
- bouncy: value is bounce speed in percent
Known damage and digging time defining groups
----------------------------------------------
@@ -888,6 +891,12 @@ minetest.get_craft_recipe(output) -> input
^ input.items = for example { stack 1, stack 2, stack 3, stack 4,
stack 5, stack 6, stack 7, stack 8, stack 9 }
^ input.items = nil if no recipe found
minetest.handle_node_drops(pos, drops, digger)
^ drops: list of itemstrings
^ Handles drops from nodes after digging: Default action is to put them into
digger's inventory
^ Can be overridden to get different functionality (eg. dropping items on
ground)
Rollbacks:
minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
@@ -928,6 +937,16 @@ minetest.after(time, func, param)
^ Call function after time seconds
^ param is optional; to pass multiple parameters, pass a table.
Server:
minetest.request_shutdown() -> request for server shutdown
minetest.get_server_status() -> server status string
Bans:
minetest.get_ban_list() -> ban list (same as minetest.get_ban_description(""))
minetest.get_ban_description(ip_or_name) -> ban description (string)
minetest.ban_player(name) -> ban a player
minetest.unban_player_or_ip(name) -> unban player or IP address
Random:
minetest.get_connected_players() -> list of ObjectRefs
minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer
@@ -1017,6 +1036,8 @@ methods:
^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
- get_perlin(seeddiff, octaves, persistence, scale)
^ Return world-specific perlin noise (int(worldseed)+seeddiff)
- clear_objects()
^ clear all objects in the environments
Deprecated:
- add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op)
@@ -1106,6 +1127,8 @@ methods:
- is_empty(listname): return true if list is empty
- get_size(listname): get size of a list
- set_size(listname, size): set size of a list
- get_width(listname): get width of a list
- set_width(listname, width): set width of list; currently used for crafting
- get_stack(listname, i): get a copy of stack index i in list
- set_stack(listname, i, stack): copy stack to index i in list
- get_list(listname): return full list

View File

@@ -1384,7 +1384,7 @@ minetest.register_abm({
srcstack:take_item()
inv:set_stack("src", 1, srcstack)
else
print("Could not insert '"..cooked.item.."'")
print("Could not insert '"..cooked.item:to_string().."'")
end
meta:set_string("src_time", 0)
end

View File

@@ -15,7 +15,7 @@
# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp
#
# A vim command to convert most of defaultsettings.cpp to conf file format:
# :'<,'>s/\tg_settings\.setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# :'<,'>s/\tsettings->setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g
# Note: Some of the settings are implemented in Lua
#
@@ -49,8 +49,14 @@
#keymap_freemove = KEY_KEY_K
#keymap_fastmove = KEY_KEY_J
#keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false
# Some (temporary) keys for debugging
#keymap_print_debug_stacks = KEY_KEY_P
#keymap_quicktune_prev = KEY_HOME
#keymap_quicktune_next = KEY_END
#keymap_quicktune_dec = KEY_NEXT
#keymap_quicktune_inc = KEY_PRIOR
# Minimum FPS
# The amount of rendered stuff is dynamically set according to this

View File

@@ -193,7 +193,6 @@ set(common_SRCS
connection.cpp
environment.cpp
server.cpp
servercommand.cpp
socket.cpp
mapblock.cpp
mapsector.cpp

View File

@@ -157,43 +157,21 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
return false;
}
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
void ClientMap::updateDrawList(video::IVideoDriver* driver)
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
g_profiler->add("CM::updateDrawList() count", 1);
INodeDefManager *nodemgr = m_gamedef->ndef();
//m_dout<<DTIME<<"Rendering map..."<<std::endl;
DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
for(core::map<v3s16, MapBlock*>::Iterator
i = m_drawlist.getIterator();
i.atEnd() == false; i++)
{
m_last_drawn_sectors.clear();
MapBlock *block = i.getNode()->getValue();
block->refDrop();
}
/*
Get time for measuring timeout.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_drawlist.clear();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
@@ -201,17 +179,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
// Use a higher fov to accomodate faster camera movements.
// Blocks are cropped better when they are drawn.
// Or maybe they aren't? Well whatever.
camera_fov *= 1.2;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min(
@@ -223,13 +199,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Number of blocks in rendering range
u32 blocks_in_range = 0;
// Number of blocks occlusion culled
@@ -242,18 +211,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0;
//u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0;
/*
Collect a set of blocks for drawing
*/
core::map<v3s16, MapBlock*> drawset;
{
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
//u32 blocks_without_stuff = 0;
for(core::map<v2s16, MapSector*>::Iterator
si = m_sectors.getIterator();
@@ -380,36 +340,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
&& d > m_control.wanted_min_range * BS)
continue;
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
// Add to set
drawset[block->getPos()] = block;
block->refGrab();
m_drawlist[block->getPos()] = block;
sector_blocks_drawn++;
blocks_drawn++;
@@ -418,8 +352,127 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
if(sector_blocks_drawn != 0)
m_last_drawn_sectors[sp] = true;
}
} // ScopeProfiler
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
if(blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
}
struct MeshBufList
{
video::SMaterial m;
core::list<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
core::list<MeshBufList> lists;
void clear()
{
lists.clear();
}
void add(scene::IMeshBuffer *buf)
{
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++){
MeshBufList &l = *i;
if(l.m == buf->getMaterial()){
l.bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.m = buf->getMaterial();
l.bufs.push_back(buf);
lists.push_back(l);
}
};
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
std::string prefix;
if(pass == scene::ESNRP_SOLID)
prefix = "CM: solid: ";
else
prefix = "CM: transparent: ";
/*
This is called two times per frame, reset on the non-transparent one
*/
if(pass == scene::ESNRP_SOLID)
{
m_last_drawn_sectors.clear();
}
/*
Get time for measuring timeout.
Measuring time is very useful for long delays when the
machine is swapping a lot.
*/
int time1 = time(0);
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
v3f camera_direction = m_camera_direction;
f32 camera_fov = m_camera_fov;
m_camera_mutex.Unlock();
/*
Get all blocks and draw all visible ones
*/
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
// Take a fair amount as we will be dropping more out later
// Umm... these additions are a bit strange but they are needed.
v3s16 p_blocks_min(
p_nodes_min.X / MAP_BLOCKSIZE - 3,
p_nodes_min.Y / MAP_BLOCKSIZE - 3,
p_nodes_min.Z / MAP_BLOCKSIZE - 3);
v3s16 p_blocks_max(
p_nodes_max.X / MAP_BLOCKSIZE + 1,
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Blocks that had mesh that would have been drawn according to
// rendering range (if max blocks limit didn't kick in)
u32 blocks_would_have_drawn = 0;
// Blocks that were drawn and had a mesh
u32 blocks_drawn = 0;
// Blocks which had a corresponding meshbuffer for this pass
u32 blocks_had_pass_meshbuf = 0;
// Blocks from which stuff was actually drawn
u32 blocks_without_stuff = 0;
/*
Draw the selected MapBlocks
*/
@@ -427,10 +480,90 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
{
ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
int timecheck_counter = 0;
MeshBufListList drawbufs;
for(core::map<v3s16, MapBlock*>::Iterator
i = drawset.getIterator();
i = m_drawlist.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
// If the mesh of the block happened to get deleted, ignore it
if(block->mesh == NULL)
continue;
float d = 0.0;
if(isBlockInSight(block->getPos(), camera_position,
camera_direction, camera_fov,
100000*BS, &d) == false)
{
continue;
}
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
/*
Get the meshbuffers of the block
*/
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
scene::SMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
u32 c = mesh->getMeshBufferCount();
for(u32 i=0; i<c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
const video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
if(transparent == is_transparent_pass)
{
if(buf->getVertexCount() == 0)
errorstream<<"Block ["<<analyze_block(block)
<<"] contains an empty meshbuf"<<std::endl;
drawbufs.add(buf);
}
}
}
}
core::list<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
for(core::list<MeshBufList>::Iterator i = lists.begin();
i != lists.end(); i++)
{
{
timecheck_counter++;
@@ -447,9 +580,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
}
MapBlock *block = i.getNode()->getValue();
MeshBufList &list = *i;
driver->setMaterial(list.m);
for(core::list<scene::IMeshBuffer*>::Iterator j = list.bufs.begin();
j != list.bufs.end(); j++)
{
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
meshbuffer_count++;
}
#if 0
/*
Draw the faces of the block
*/
@@ -494,17 +638,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
else
blocks_without_stuff++;
}
#endif
}
} // ScopeProfiler
// Log only on solid pass because values are the same
if(pass == scene::ESNRP_SOLID){
g_profiler->avg("CM: blocks in range", blocks_in_range);
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
if(blocks_in_range != 0)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
g_profiler->avg("CM: animated meshes", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
}

View File

@@ -113,7 +113,8 @@ class ClientMap : public Map, public scene::ISceneNode
{
return m_box;
}
void updateDrawList(video::IVideoDriver* driver);
void renderMap(video::IVideoDriver* driver, s32 pass);
int getBackgroundBrightness(float max_d, u32 daylight_factor,
@@ -141,6 +142,8 @@ class ClientMap : public Map, public scene::ISceneNode
v3f m_camera_direction;
f32 m_camera_fov;
JMutex m_camera_mutex;
core::map<v3s16, MapBlock*> m_drawlist;
core::map<v2s16, bool> m_last_drawn_sectors;
};

View File

@@ -65,9 +65,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
TOSERVER_INVENTORY_FIELDS
16-bit node ids
TOCLIENT_DETACHED_INVENTORY
PROTOCOL_VERSION 13:
InventoryList field "Width" (deserialization fails with old versions)
*/
#define PROTOCOL_VERSION 12
#define PROTOCOL_VERSION 13
#define PROTOCOL_ID 0x4f457403

View File

@@ -211,6 +211,8 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
std::vector<aabb3f> cboxes;
std::vector<bool> is_unloaded;
std::vector<bool> is_step_up;
std::vector<int> bouncy_values;
std::vector<v3s16> node_positions;
{
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);
@@ -228,11 +230,14 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
for(s16 y = min_y; y <= max_y; y++)
for(s16 z = min_z; z <= max_z; z++)
{
v3s16 p(x,y,z);
try{
// Object collides into walkable nodes
MapNode n = map->getNode(v3s16(x,y,z));
if(gamedef->getNodeDefManager()->get(n).walkable == false)
MapNode n = map->getNode(p);
const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
if(f.walkable == false)
continue;
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
for(std::vector<aabb3f>::iterator
@@ -245,21 +250,27 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cboxes.push_back(box);
is_unloaded.push_back(false);
is_step_up.push_back(false);
bouncy_values.push_back(n_bouncy_value);
node_positions.push_back(p);
}
}
catch(InvalidPositionException &e)
{
// Collide with unloaded nodes
aabb3f box = getNodeBox(v3s16(x,y,z), BS);
aabb3f box = getNodeBox(p, BS);
cboxes.push_back(box);
is_unloaded.push_back(true);
is_step_up.push_back(false);
bouncy_values.push_back(0);
node_positions.push_back(p);
}
}
} // tt2
assert(cboxes.size() == is_unloaded.size());
assert(cboxes.size() == is_step_up.size());
assert(cboxes.size() == bouncy_values.size());
assert(cboxes.size() == node_positions.size());
/*
Collision detection
@@ -342,6 +353,10 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
cbox.MaxEdge.Y - movingbox.MinEdge.Y,
d));
// Get bounce multiplier
bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;
// Move to the point of collision and reduce dtime by nearest_dtime
if(nearest_dtime < 0)
{
@@ -361,30 +376,58 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
bool is_collision = true;
if(is_unloaded[nearest_boxindex])
is_collision = false;
CollisionInfo info;
info.type = COLLISION_NODE;
info.node_p = node_positions[nearest_boxindex];
info.bouncy = bouncy;
info.old_speed = speed_f;
// Set the speed component that caused the collision to zero
if(step_up)
{
// Special case: Handle stairs
is_step_up[nearest_boxindex] = true;
is_collision = false;
}
else if(nearest_collided == 0) // X
{
speed_f.X = 0;
if(fabs(speed_f.X) > BS*3)
speed_f.X *= bounce;
else
speed_f.X = 0;
result.collides = true;
result.collides_xz = true;
}
else if(nearest_collided == 1) // Y
{
speed_f.Y = 0;
if(fabs(speed_f.Y) > BS*3)
speed_f.Y *= bounce;
else
speed_f.Y = 0;
result.collides = true;
}
else if(nearest_collided == 2) // Z
{
speed_f.Z = 0;
if(fabs(speed_f.Z) > BS*3)
speed_f.Z *= bounce;
else
speed_f.Z = 0;
result.collides = true;
result.collides_xz = true;
}
info.new_speed = speed_f;
if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
is_collision = false;
if(is_collision){
result.collisions.push_back(info);
}
}
}

View File

@@ -26,12 +26,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Map;
class IGameDef;
enum CollisionType
{
COLLISION_NODE
};
struct CollisionInfo
{
enum CollisionType type;
v3s16 node_p; // COLLISION_NODE
bool bouncy;
v3f old_speed;
v3f new_speed;
CollisionInfo():
type(COLLISION_NODE),
node_p(-32768,-32768,-32768),
bouncy(false),
old_speed(0,0,0),
new_speed(0,0,0)
{}
};
struct collisionMoveResult
{
bool touching_ground;
bool collides;
bool collides_xz;
bool standing_on_unloaded;
std::vector<CollisionInfo> collisions;
collisionMoveResult():
touching_ground(false),
@@ -72,16 +95,5 @@ bool wouldCollideWithCeiling(
f32 y_increase, f32 d);
enum CollisionType
{
COLLISION_FALL
};
struct CollisionInfo
{
CollisionType t;
f32 speed;
};
#endif

View File

@@ -52,6 +52,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_toggle_profiler", "KEY_F6");
settings->setDefault("keymap_increase_viewing_range_min", "+");
settings->setDefault("keymap_decrease_viewing_range_min", "-");
settings->setDefault("aux1_descends", "false");
// Some (temporary) keys for debugging
settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
settings->setDefault("keymap_quicktune_prev", "KEY_HOME");

View File

@@ -1087,7 +1087,8 @@ void ServerEnvironment::step(float dtime)
i = elapsed_timers.begin();
i != elapsed_timers.end(); i++){
n = block->getNodeNoEx(i->first);
if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
p = i->first + block->getPosRelative();
if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
}
}
@@ -2033,19 +2034,32 @@ void ClientEnvironment::step(float dtime)
i != player_collisions.end(); i++)
{
CollisionInfo &info = *i;
if(info.t == COLLISION_FALL)
v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage
// (because otherwise walking against something in fast_move kills you)
if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
continue;
// Get rid of other components
speed_diff.X = 0;
speed_diff.Z = 0;
f32 pre_factor = 1; // 1 hp per node/s
f32 tolerance = BS*14; // 5 without damage
f32 post_factor = 1; // 1 hp per node/s
if(info.type == COLLISION_NODE)
{
//f32 tolerance = BS*10; // 2 without damage
//f32 tolerance = BS*12; // 3 without damage
f32 tolerance = BS*14; // 5 without damage
f32 factor = 1;
if(info.speed > tolerance)
{
f32 damage_f = (info.speed - tolerance)/BS*factor;
u16 damage = (u16)(damage_f+0.5);
if(damage != 0)
damageLocalPlayer(damage, true);
}
const ContentFeatures &f = m_gamedef->ndef()->
get(m_map->getNodeNoEx(info.node_p));
// Determine fall damage multiplier
int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
pre_factor = 1.0 + (float)addp/100.0;
}
float speed = pre_factor * speed_diff.getLength();
if(speed > tolerance)
{
f32 damage_f = (speed - tolerance)/BS * post_factor;
u16 damage = (u16)(damage_f+0.5);
if(damage != 0)
damageLocalPlayer(damage, true);
}
}

View File

@@ -1235,6 +1235,9 @@ void the_game(
float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10;
float update_draw_list_timer = 0.0;
v3f update_draw_list_last_cam_dir;
bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false;
@@ -2697,7 +2700,19 @@ void the_game(
item = mlist->getItem(client.getPlayerItem());
camera.wield(item);
}
/*
Update block draw list every 200ms or when camera direction has
changed much
*/
update_draw_list_timer += dtime;
if(update_draw_list_timer >= 0.2 ||
update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2){
update_draw_list_timer = 0;
client.getEnv().getClientMap().updateDrawList(driver);
update_draw_list_last_cam_dir = camera_direction;
}
/*
Drawing begins
*/

View File

@@ -733,6 +733,54 @@ void GUIFormSpecMenu::drawMenu()
void GUIFormSpecMenu::updateSelectedItem()
{
// WARNING: BLACK MAGIC
// See if there is a stack suited for our current guess.
// If such stack does not exist, clear the guess.
if(m_selected_content_guess.name != "")
{
bool found = false;
for(u32 i=0; i<m_inventorylists.size() && !found; i++){
const ListDrawSpec &s = m_inventorylists[i];
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv)
continue;
InventoryList *list = inv->getList(s.listname);
if(!list)
continue;
for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
u32 item_i = i + s.start_item_i;
if(item_i >= list->getSize())
continue;
ItemStack stack = list->getItem(item_i);
if(stack.name == m_selected_content_guess.name &&
stack.count == m_selected_content_guess.count){
found = true;
if(m_selected_item){
// If guessed stack is already selected, all is fine
if(m_selected_item->inventoryloc == s.inventoryloc &&
m_selected_item->listname == s.listname &&
m_selected_item->i == (s32)item_i &&
m_selected_amount == stack.count){
break;
}
delete m_selected_item;
m_selected_item = NULL;
}
infostream<<"Client: Changing selected content guess to "
<<s.inventoryloc.dump()<<" "<<s.listname
<<" "<<item_i<<std::endl;
m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
m_selected_amount = stack.count;
break;
}
}
}
if(!found){
infostream<<"Client: Discarding selected content guess: "
<<m_selected_content_guess.getItemString()<<std::endl;
m_selected_content_guess.name = "";
}
}
// If the selected stack has become empty for some reason, deselect it.
// If the selected stack has become smaller, adjust m_selected_amount.
if(m_selected_item)
@@ -1054,21 +1102,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
if(leftover.count == stack_from.count)
// If source stack cannot be added to destination stack at all,
// they are swapped
if(leftover.count == stack_from.count && leftover.name == stack_from.name)
{
// Swap the stacks
m_selected_amount -= stack_to.count;
m_selected_amount = stack_to.count;
// In case the server doesn't directly swap them but instead
// moves stack_to somewhere else, set this
m_selected_content_guess = stack_to;
m_selected_content_guess_inventory = s.inventoryloc;
}
// Source stack goes fully into destination stack
else if(leftover.empty())
{
// Item fits
m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
}
// Source stack goes partly into destination stack
else
{
// Item only fits partially
move_amount -= leftover.count;
m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
}
infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
@@ -1084,6 +1139,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
else if(drop_amount > 0)
{
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_DROP
assert(m_selected_item && m_selected_item->isValid());
@@ -1107,6 +1164,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
else if(craft_amount > 0)
{
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT
assert(s.isValid());
@@ -1126,6 +1185,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_item = NULL;
m_selected_amount = 0;
m_selected_dragging = false;
m_selected_content_guess = ItemStack();
}
}
if(event.EventType==EET_GUI_EVENT)

View File

@@ -212,6 +212,12 @@ class GUIFormSpecMenu : public GUIModalMenu
ItemSpec *m_selected_item;
u32 m_selected_amount;
bool m_selected_dragging;
// WARNING: BLACK MAGIC
// Used to guess and keep up with some special things the server can do.
// If name is "", no guess exists.
ItemStack m_selected_content_guess;
InventoryLocation m_selected_content_guess_inventory;
v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element;

View File

@@ -30,11 +30,14 @@
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "settings.h"
#include <algorithm>
#define KMaxButtonPerColumns 12
enum
{
GUI_ID_BACK_BUTTON = 101, GUI_ID_ABORT_BUTTON, GUI_ID_SCROLL_BAR,
//buttons
// buttons
GUI_ID_KEY_FORWARD_BUTTON,
GUI_ID_KEY_BACKWARD_BUTTON,
GUI_ID_KEY_LEFT_BUTTON,
@@ -50,15 +53,21 @@ enum
GUI_ID_KEY_DROP_BUTTON,
GUI_ID_KEY_INVENTORY_BUTTON,
GUI_ID_KEY_DUMP_BUTTON,
GUI_ID_KEY_RANGE_BUTTON
GUI_ID_KEY_RANGE_BUTTON,
// other
GUI_ID_CB_AUX1_DESCENDS,
};
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
GUIModalMenu(env, parent, id, menumgr)
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
GUIModalMenu(env, parent, id, menumgr)
{
shift_down = false;
activeKey = -1;
this->key_used_text = NULL;
init_keys();
for(size_t i=0; i<key_settings.size(); i++)
this->key_used.push_back(key_settings.at(i)->key);
}
GUIKeyChangeMenu::~GUIKeyChangeMenu()
@@ -71,12 +80,12 @@ void GUIKeyChangeMenu::removeChildren()
const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy;
for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
!= children.end(); i++)
!= children.end(); i++)
{
children_copy.push_back(*i);
}
for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
!= children_copy.end(); i++)
!= children_copy.end(); i++)
{
(*i)->remove();
}
@@ -84,20 +93,12 @@ void GUIKeyChangeMenu::removeChildren()
void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
/*
Remove stuff
*/
removeChildren();
/*
Calculate new sizes and positions
*/
v2s32 size(620, 430);
core::rect < s32 > rect(screensize.X / 2 - size.X / 2,
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
screensize.Y / 2 + size.Y / 2);
screensize.Y / 2 - size.Y / 2, screensize.X / 2 + size.X / 2,
screensize.Y / 2 + size.Y / 2);
DesiredRect = rect;
recalculateAbsolutePosition(false);
@@ -109,271 +110,60 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
rect += topleft + v2s32(25, 3);
//gui::IGUIStaticText *t =
Environment->addStaticText(wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)"),
rect, false, true, this, -1);
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
// Build buttons
v2s32 offset(25, 60);
// buttons
for(size_t i = 0; i < key_settings.size(); i++)
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Forward"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
key_setting *k = key_settings.at(i);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(k->button_name, rect, false, true, this, -1);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
k->button = Environment->addButton(rect, this, k->id, wgettext(k->key.name()));
}
if(i + 1 == KMaxButtonPerColumns)
offset = v2s32(250, 60);
else
offset += v2s32(0, 25);
}
{
s32 option_x = offset.X + 10;
s32 option_y = offset.Y;
u32 option_w = 180;
{
core::rect<s32> rect(0, 0, option_w, 30);
rect += topleft + v2s32(option_x, option_y);
Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down"));
}
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->forward = Environment->addButton(rect, this,
GUI_ID_KEY_FORWARD_BUTTON,
wgettext(key_forward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Backward"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->backward = Environment->addButton(rect, this,
GUI_ID_KEY_BACKWARD_BUTTON,
wgettext(key_backward.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Left"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->left = Environment->addButton(rect, this, GUI_ID_KEY_LEFT_BUTTON,
wgettext(key_left.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Right"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->right = Environment->addButton(rect, this,
GUI_ID_KEY_RIGHT_BUTTON,
wgettext(key_right.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Use"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->use = Environment->addButton(rect, this, GUI_ID_KEY_USE_BUTTON,
wgettext(key_use.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Sneak"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->sneak = Environment->addButton(rect, this,
GUI_ID_KEY_SNEAK_BUTTON,
wgettext(key_sneak.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Jump"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->jump = Environment->addButton(rect, this, GUI_ID_KEY_JUMP_BUTTON,
wgettext(key_jump.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Drop"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dropbtn = Environment->addButton(rect, this, GUI_ID_KEY_DROP_BUTTON,
wgettext(key_drop.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Inventory"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->inventory = Environment->addButton(rect, this,
GUI_ID_KEY_INVENTORY_BUTTON,
wgettext(key_inventory.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Chat"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->chat = Environment->addButton(rect, this, GUI_ID_KEY_CHAT_BUTTON,
wgettext(key_chat.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Command"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->cmd = Environment->addButton(rect, this, GUI_ID_KEY_CMD_BUTTON,
wgettext(key_cmd.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Console"), rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->console = Environment->addButton(rect, this, GUI_ID_KEY_CONSOLE_BUTTON,
wgettext(key_console.name()));
}
//next col
offset = v2s32(250, 60);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fly"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fly = Environment->addButton(rect, this, GUI_ID_KEY_FLY_BUTTON,
wgettext(key_fly.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Toggle fast"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->fast = Environment->addButton(rect, this, GUI_ID_KEY_FAST_BUTTON,
wgettext(key_fast.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Range select"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->range = Environment->addButton(rect, this,
GUI_ID_KEY_RANGE_BUTTON,
wgettext(key_range.name()));
}
offset += v2s32(0, 25);
{
core::rect < s32 > rect(0, 0, 100, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(wgettext("Print stacks"),
rect, false, true, this, -1);
//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
this->dump = Environment->addButton(rect, this, GUI_ID_KEY_DUMP_BUTTON,
wgettext(key_dump.name()));
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
wgettext("Save"));
wgettext("Save"));
}
{
core::rect < s32 > rect(0, 0, 100, 30);
rect += topleft + v2s32(size.X - 100 - 20 - 100 - 20, size.Y - 40);
Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
wgettext("Cancel"));
wgettext("Cancel"));
}
changeCtype("C");
}
void GUIKeyChangeMenu::drawMenu()
@@ -396,102 +186,32 @@ void GUIKeyChangeMenu::drawMenu()
bool GUIKeyChangeMenu::acceptInput()
{
g_settings->set("keymap_forward", key_forward.sym());
g_settings->set("keymap_backward", key_backward.sym());
g_settings->set("keymap_left", key_left.sym());
g_settings->set("keymap_right", key_right.sym());
g_settings->set("keymap_jump", key_jump.sym());
g_settings->set("keymap_sneak", key_sneak.sym());
g_settings->set("keymap_drop", key_drop.sym());
g_settings->set("keymap_inventory", key_inventory.sym());
g_settings->set("keymap_chat", key_chat.sym());
g_settings->set("keymap_cmd", key_cmd.sym());
g_settings->set("keymap_console", key_console.sym());
g_settings->set("keymap_rangeselect", key_range.sym());
g_settings->set("keymap_freemove", key_fly.sym());
g_settings->set("keymap_fastmove", key_fast.sym());
g_settings->set("keymap_special1", key_use.sym());
g_settings->set("keymap_print_debug_stacks", key_dump.sym());
for(size_t i = 0; i < key_settings.size(); i++)
{
key_setting *k = key_settings.at(i);
g_settings->set(k->setting_name, k->key.sym());
}
{
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
}
clearKeyCache();
return true;
}
void GUIKeyChangeMenu::init_keys()
{
key_forward = getKeySetting("keymap_forward");
key_backward = getKeySetting("keymap_backward");
key_left = getKeySetting("keymap_left");
key_right = getKeySetting("keymap_right");
key_jump = getKeySetting("keymap_jump");
key_sneak = getKeySetting("keymap_sneak");
key_drop = getKeySetting("keymap_drop");
key_inventory = getKeySetting("keymap_inventory");
key_chat = getKeySetting("keymap_chat");
key_cmd = getKeySetting("keymap_cmd");
key_console = getKeySetting("keymap_console");
key_range = getKeySetting("keymap_rangeselect");
key_fly = getKeySetting("keymap_freemove");
key_fast = getKeySetting("keymap_fastmove");
key_use = getKeySetting("keymap_special1");
key_dump = getKeySetting("keymap_print_debug_stacks");
}
bool GUIKeyChangeMenu::resetMenu()
{
if (activeKey >= 0)
{
switch (activeKey)
for(size_t i = 0; i < key_settings.size(); i++)
{
case GUI_ID_KEY_FORWARD_BUTTON:
this->forward->setText(
wgettext(key_forward.name()));
break;
case GUI_ID_KEY_BACKWARD_BUTTON:
this->backward->setText(
wgettext(key_backward.name()));
break;
case GUI_ID_KEY_LEFT_BUTTON:
this->left->setText(wgettext(key_left.name()));
break;
case GUI_ID_KEY_RIGHT_BUTTON:
this->right->setText(wgettext(key_right.name()));
break;
case GUI_ID_KEY_JUMP_BUTTON:
this->jump->setText(wgettext(key_jump.name()));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
this->sneak->setText(wgettext(key_sneak.name()));
break;
case GUI_ID_KEY_DROP_BUTTON:
this->dropbtn->setText(wgettext(key_drop.name()));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
this->inventory->setText(
wgettext(key_inventory.name()));
break;
case GUI_ID_KEY_CHAT_BUTTON:
this->chat->setText(wgettext(key_chat.name()));
break;
case GUI_ID_KEY_CMD_BUTTON:
this->cmd->setText(wgettext(key_cmd.name()));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
this->console->setText(wgettext(key_console.name()));
break;
case GUI_ID_KEY_RANGE_BUTTON:
this->range->setText(wgettext(key_range.name()));
break;
case GUI_ID_KEY_FLY_BUTTON:
this->fly->setText(wgettext(key_fly.name()));
break;
case GUI_ID_KEY_FAST_BUTTON:
this->fast->setText(wgettext(key_fast.name()));
break;
case GUI_ID_KEY_USE_BUTTON:
this->use->setText(wgettext(key_use.name()));
break;
case GUI_ID_KEY_DUMP_BUTTON:
this->dump->setText(wgettext(key_dump.name()));
break;
key_setting *k = key_settings.at(i);
if(k->id == activeKey)
{
k->button->setText(wgettext(k->key.name()));
break;
}
}
activeKey = -1;
return false;
@@ -501,104 +221,72 @@ bool GUIKeyChangeMenu::resetMenu()
bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
{
if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
&& event.KeyInput.PressedDown)
&& event.KeyInput.PressedDown)
{
changeCtype("");
KeyPress kp(event.KeyInput);
bool prefer_character = shift_down;
KeyPress kp(event.KeyInput, prefer_character);
bool shift_went_down = false;
if(!shift_down &&
(event.KeyInput.Key == irr::KEY_SHIFT ||
event.KeyInput.Key == irr::KEY_LSHIFT ||
event.KeyInput.Key == irr::KEY_RSHIFT))
shift_went_down = true;
if (activeKey == GUI_ID_KEY_FORWARD_BUTTON)
// Remove Key already in use message
if(this->key_used_text)
{
this->forward->setText(wgettext(kp.name()));
this->key_forward = kp;
this->key_used_text->remove();
this->key_used_text = NULL;
}
else if (activeKey == GUI_ID_KEY_BACKWARD_BUTTON)
// Display Key already in use message
if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
{
this->backward->setText(wgettext(kp.name()));
this->key_backward = kp;
core::rect < s32 > rect(0, 0, 600, 40);
rect += v2s32(0, 0) + v2s32(25, 30);
this->key_used_text = Environment->addStaticText(wgettext("Key already in use"),
rect, false, true, this, -1);
//infostream << "Key already in use" << std::endl;
}
else if (activeKey == GUI_ID_KEY_LEFT_BUTTON)
// But go on
{
this->left->setText(wgettext(kp.name()));
this->key_left = kp;
key_setting *k=NULL;
for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == activeKey)
{
k = key_settings.at(i);
break;
}
}
assert(k);
k->key = kp;
k->button->setText(wgettext(k->key.name()));
this->key_used.push_back(kp);
changeCtype("C");
// Allow characters made with shift
if(shift_went_down){
shift_down = true;
return false;
}else{
activeKey = -1;
return true;
}
}
else if (activeKey == GUI_ID_KEY_RIGHT_BUTTON)
{
this->right->setText(wgettext(kp.name()));
this->key_right = kp;
}
else if (activeKey == GUI_ID_KEY_JUMP_BUTTON)
{
this->jump->setText(wgettext(kp.name()));
this->key_jump = kp;
}
else if (activeKey == GUI_ID_KEY_SNEAK_BUTTON)
{
this->sneak->setText(wgettext(kp.name()));
this->key_sneak = kp;
}
else if (activeKey == GUI_ID_KEY_DROP_BUTTON)
{
this->dropbtn->setText(wgettext(kp.name()));
this->key_drop = kp;
}
else if (activeKey == GUI_ID_KEY_INVENTORY_BUTTON)
{
this->inventory->setText(wgettext(kp.name()));
this->key_inventory = kp;
}
else if (activeKey == GUI_ID_KEY_CHAT_BUTTON)
{
this->chat->setText(wgettext(kp.name()));
this->key_chat = kp;
}
else if (activeKey == GUI_ID_KEY_CMD_BUTTON)
{
this->cmd->setText(wgettext(kp.name()));
this->key_cmd = kp;
}
else if (activeKey == GUI_ID_KEY_CONSOLE_BUTTON)
{
this->console->setText(wgettext(kp.name()));
this->key_console = kp;
}
else if (activeKey == GUI_ID_KEY_RANGE_BUTTON)
{
this->range->setText(wgettext(kp.name()));
this->key_range = kp;
}
else if (activeKey == GUI_ID_KEY_FLY_BUTTON)
{
this->fly->setText(wgettext(kp.name()));
this->key_fly = kp;
}
else if (activeKey == GUI_ID_KEY_FAST_BUTTON)
{
this->fast->setText(wgettext(kp.name()));
this->key_fast = kp;
}
else if (activeKey == GUI_ID_KEY_USE_BUTTON)
{
this->use->setText(wgettext(kp.name()));
this->key_use = kp;
}
else if (activeKey == GUI_ID_KEY_DUMP_BUTTON)
{
this->dump->setText(wgettext(kp.name()));
this->key_dump = kp;
}
changeCtype("C");
activeKey = -1;
return true;
}
if (event.EventType == EET_GUI_EVENT)
{
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
&& isVisible())
{
if (!canTakeFocus(event.GUIEvent.Element))
{
dstream << "GUIMainMenu: Not allowing focus change."
<< std::endl;
<< std::endl;
// Returning true disables focus change
return true;
}
@@ -606,106 +294,74 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED)
{
if(event.GUIEvent.Caller->getID() != GUI_ID_BACK_BUTTON &&
event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
event.GUIEvent.Caller->getID() != GUI_ID_ABORT_BUTTON)
{
changeCtype("");
}
switch (event.GUIEvent.Caller->getID())
{
case GUI_ID_BACK_BUTTON: //back
acceptInput();
quitMenu();
return true;
case GUI_ID_ABORT_BUTTON: //abort
quitMenu();
return true;
case GUI_ID_KEY_FORWARD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->forward->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_BACKWARD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->backward->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_LEFT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->left->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_RIGHT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->right->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_USE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->use->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FLY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fly->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_FAST_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->fast->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_JUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->jump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DROP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dropbtn->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CHAT_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->chat->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CMD_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->cmd->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_CONSOLE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->console->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_SNEAK_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->sneak->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_INVENTORY_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->inventory->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_DUMP_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->dump->setText(wgettext("press Key"));
break;
case GUI_ID_KEY_RANGE_BUTTON:
resetMenu();
activeKey = event.GUIEvent.Caller->getID();
this->range->setText(wgettext("press Key"));
break;
case GUI_ID_BACK_BUTTON: //back
acceptInput();
quitMenu();
return true;
case GUI_ID_ABORT_BUTTON: //abort
quitMenu();
return true;
default:
key_setting *k = NULL;
for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == event.GUIEvent.Caller->getID())
{
k = key_settings.at(i);
break;
}
}
assert(k);
resetMenu();
shift_down = false;
activeKey = event.GUIEvent.Caller->getID();
k->button->setText(wgettext("press key"));
this->key_used.erase(std::remove(this->key_used.begin(),
this->key_used.end(), k->key), this->key_used.end());
break;
}
Environment->setFocus(this);
//Buttons
changeCtype("C");
}
}
return Parent ? Parent->OnEvent(event) : false;
}
void GUIKeyChangeMenu::add_key(int id, std::string button_name, std::string setting_name)
{
key_setting *k = new key_setting;
k->id = id;
k->button_name = wgettext(button_name.c_str());
k->setting_name = setting_name;
k->key = getKeySetting(k->setting_name.c_str());
key_settings.push_back(k);
}
void GUIKeyChangeMenu::init_keys()
{
this->add_key(GUI_ID_KEY_FORWARD_BUTTON, "Forward", "keymap_forward");
this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, "Backward", "keymap_backward");
this->add_key(GUI_ID_KEY_LEFT_BUTTON, "Left", "keymap_left");
this->add_key(GUI_ID_KEY_RIGHT_BUTTON, "Right", "keymap_right");
this->add_key(GUI_ID_KEY_USE_BUTTON, "Use", "keymap_special1");
this->add_key(GUI_ID_KEY_JUMP_BUTTON, "Jump", "keymap_jump");
this->add_key(GUI_ID_KEY_SNEAK_BUTTON, "Sneak", "keymap_sneak");
this->add_key(GUI_ID_KEY_DROP_BUTTON, "Drop", "keymap_drop");
this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, "Inventory", "keymap_inventory");
this->add_key(GUI_ID_KEY_CHAT_BUTTON, "Chat", "keymap_chat");
this->add_key(GUI_ID_KEY_CMD_BUTTON, "Command", "keymap_cmd");
this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console");
this->add_key(GUI_ID_KEY_FLY_BUTTON, "Toggle fly", "keymap_freemove");
this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect");
this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
}

View File

@@ -28,6 +28,16 @@
#include "gettext.h"
#include "keycode.h"
#include <string>
#include <vector>
typedef struct {
int id;
wchar_t *button_name;
KeyPress key;
std::string setting_name;
gui::IGUIButton *button;
} key_setting;
class GUIKeyChangeMenu: public GUIModalMenu
{
@@ -54,40 +64,15 @@ class GUIKeyChangeMenu: public GUIModalMenu
bool resetMenu();
gui::IGUIButton *forward;
gui::IGUIButton *backward;
gui::IGUIButton *left;
gui::IGUIButton *right;
gui::IGUIButton *use;
gui::IGUIButton *sneak;
gui::IGUIButton *jump;
gui::IGUIButton *dropbtn;
gui::IGUIButton *inventory;
gui::IGUIButton *fly;
gui::IGUIButton *fast;
gui::IGUIButton *range;
gui::IGUIButton *dump;
gui::IGUIButton *chat;
gui::IGUIButton *cmd;
gui::IGUIButton *console;
void add_key(int id, std::string setting_name, std::string button_name);
bool shift_down;
s32 activeKey;
KeyPress key_forward;
KeyPress key_backward;
KeyPress key_left;
KeyPress key_right;
KeyPress key_use;
KeyPress key_sneak;
KeyPress key_jump;
KeyPress key_drop;
KeyPress key_inventory;
KeyPress key_fly;
KeyPress key_fast;
KeyPress key_range;
KeyPress key_chat;
KeyPress key_cmd;
KeyPress key_console;
KeyPress key_dump;
std::vector<KeyPress> key_used;
gui::IGUIStaticText *key_used_text;
std::vector<key_setting *> key_settings;
};
#endif

View File

@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tile.h" // getTexturePath
#include "filesys.h"
#include "util/string.h"
#include "subgame.h"
struct CreateWorldDestMainMenu : public CreateWorldDest
{
@@ -47,6 +48,22 @@ struct CreateWorldDestMainMenu : public CreateWorldDest
{}
void accepted(std::wstring name, std::string gameid)
{
std::string name_narrow = wide_to_narrow(name);
if(!string_allowed_blacklist(name_narrow, WORLDNAME_BLACKLISTED_CHARS))
{
m_menu->displayMessageMenu(wgettext("Cannot create world: Name contains invalid characters"));
return;
}
std::vector<WorldSpec> worlds = getAvailableWorlds();
for(std::vector<WorldSpec>::iterator i = worlds.begin();
i != worlds.end(); i++)
{
if((*i).name == name_narrow)
{
m_menu->displayMessageMenu(wgettext("Cannot create world: A world by this name already exists"));
return;
}
}
m_menu->createNewWorld(name, gameid);
}
GUIMainMenu *m_menu;
@@ -929,3 +946,7 @@ int GUIMainMenu::getTab()
return TAB_SINGLEPLAYER; // Default
}
void GUIMainMenu::displayMessageMenu(std::wstring msg)
{
(new GUIMessageMenu(env, parent, -1, menumgr, msg))->drop();
}

View File

@@ -92,6 +92,7 @@ class GUIMainMenu : public GUIModalMenu
void createNewWorld(std::wstring name, std::string gameid);
void deleteWorld(const std::vector<std::string> &paths);
int getTab();
void displayMessageMenu(std::wstring msg);
private:
MainMenuData *m_data;

View File

@@ -431,6 +431,7 @@ InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemde
{
m_name = name;
m_size = size;
m_width = 0;
m_itemdef = itemdef;
clearItems();
//m_dirty = false;
@@ -459,6 +460,11 @@ void InventoryList::setSize(u32 newsize)
m_size = newsize;
}
void InventoryList::setWidth(u32 newwidth)
{
m_width = newwidth;
}
void InventoryList::setName(const std::string &name)
{
m_name = name;
@@ -468,6 +474,8 @@ void InventoryList::serialize(std::ostream &os) const
{
//os.imbue(std::locale("C"));
os<<"Width "<<m_width<<"\n";
for(u32 i=0; i<m_items.size(); i++)
{
const ItemStack &item = m_items[i];
@@ -492,6 +500,7 @@ void InventoryList::deSerialize(std::istream &is)
clearItems();
u32 item_i = 0;
m_width = 0;
for(;;)
{
@@ -513,6 +522,12 @@ void InventoryList::deSerialize(std::istream &is)
{
break;
}
else if(name == "Width")
{
iss >> m_width;
if (iss.fail())
throw SerializationError("incorrect width property");
}
else if(name == "Item")
{
if(item_i > getSize() - 1)
@@ -527,10 +542,6 @@ void InventoryList::deSerialize(std::istream &is)
throw SerializationError("too many items");
m_items[item_i++].clear();
}
else
{
throw SerializationError("Unknown inventory identifier");
}
}
}
@@ -543,6 +554,7 @@ InventoryList & InventoryList::operator = (const InventoryList &other)
{
m_items = other.m_items;
m_size = other.m_size;
m_width = other.m_width;
m_name = other.m_name;
m_itemdef = other.m_itemdef;
//setDirty(true);
@@ -560,6 +572,11 @@ u32 InventoryList::getSize() const
return m_items.size();
}
u32 InventoryList::getWidth() const
{
return m_width;
}
u32 InventoryList::getUsedSlots() const
{
u32 num = 0;
@@ -886,10 +903,6 @@ void Inventory::deSerialize(std::istream &is)
m_lists.push_back(list);
}
else
{
throw SerializationError("Unknown inventory identifier");
}
}
}

View File

@@ -176,6 +176,7 @@ class InventoryList
~InventoryList();
void clearItems();
void setSize(u32 newsize);
void setWidth(u32 newWidth);
void setName(const std::string &name);
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
@@ -185,6 +186,7 @@ class InventoryList
const std::string &getName() const;
u32 getSize() const;
u32 getWidth() const;
// Count used slots
u32 getUsedSlots() const;
u32 getFreeSlots() const;
@@ -240,7 +242,7 @@ class InventoryList
private:
std::vector<ItemStack> m_items;
u32 m_size;
u32 m_size, m_width;
std::string m_name;
IItemDefManager *m_itemdef;
};

View File

@@ -332,6 +332,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
// If source is infinite, reset it's stack
if(src_can_take_count == -1){
// If destination stack is of different type and there are leftover
// items, attempt to put the leftover items to a different place in the
// destination inventory.
// The client-side GUI will try to guess if this happens.
if(from_stack_was.name != to_stack_was.name){
for(u32 i=0; i<list_to->getSize(); i++){
if(list_to->getItem(i).empty()){
list_to->changeItem(i, to_stack_was);
break;
}
}
}
list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was);
}
@@ -339,6 +351,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
if(dst_can_put_count == -1){
list_to->deleteItem(to_i);
list_to->addItem(to_i, to_stack_was);
list_from->deleteItem(from_i);
list_from->addItem(from_i, from_stack_was);
list_from->takeItem(from_i, count);
}
@@ -360,7 +374,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
IRollbackReportSink *rollback = gamedef->rollback();
// If source is not infinite, record item take
if(!src_can_take_count != -1){
if(src_can_take_count != -1){
RollbackAction action;
std::string loc;
{
@@ -373,7 +387,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
rollback->reportAction(action);
}
// If destination is not infinite, record item put
if(!dst_can_put_count != -1){
if(dst_can_put_count != -1){
RollbackAction action;
std::string loc;
{
@@ -575,6 +589,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
// Drop the item
ItemStack item1 = list_from->getItem(from_i);
item1.count = take_count;
if(scriptapi_item_on_drop(player->getEnv()->getLua(), item1, player,
player->getBasePosition() + v3f(0,1,0)))
{
@@ -633,7 +648,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
IRollbackReportSink *rollback = gamedef->rollback();
// If source is not infinite, record item take
if(!src_can_take_count != -1){
if(src_can_take_count != -1){
RollbackAction action;
std::string loc;
{
@@ -768,18 +783,16 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
result.clear();
// TODO: Allow different sizes of crafting grids
// Get the InventoryList in which we will operate
InventoryList *clist = inv->getList("craft");
if(!clist || clist->getSize() != 9)
if(!clist)
return false;
// Mangle crafting grid to an another format
CraftInput ci;
ci.method = CRAFT_METHOD_NORMAL;
ci.width = 3;
for(u16 i=0; i<9; i++)
ci.width = clist->getWidth() ? clist->getWidth() : 3;
for(u16 i=0; i<clist->getSize(); i++)
ci.items.push_back(clist->getItem(i));
// Find out what is crafted and add it to result item slot
@@ -792,7 +805,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
if(found && decrementInput)
{
// CraftInput has been changed, apply changes in clist
for(u16 i=0; i<9; i++)
for(u16 i=0; i<clist->getSize(); i++)
{
clist->changeItem(i, ci.items[i]);
}

View File

@@ -66,6 +66,29 @@ struct InventoryLocation
name = name_;
}
bool operator==(const InventoryLocation &other) const
{
if(type != other.type)
return false;
switch(type){
case UNDEFINED:
return false;
case CURRENT_PLAYER:
return true;
case PLAYER:
return (name == other.name);
case NODEMETA:
return (p == other.p);
case DETACHED:
return (name == other.name);
}
return false;
}
bool operator!=(const InventoryLocation &other) const
{
return !(*this == other);
}
void applyCurrentPlayer(const std::string &name_)
{
if(type == CURRENT_PLAYER)

View File

@@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "main.h" // For g_settings
#include "exceptions.h"
#include "settings.h"
#include "log.h"
#include "hex.h"
class UnknownKeycode : public BaseException
{
@@ -286,16 +288,30 @@ KeyPress::KeyPress(const char *name)
m_name = name[0];
}
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in)
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
{
Key = in.Key;
Char = in.Char;
if(prefer_character){
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
if(written > 0){
infostream<<"KeyPress: Preferring character for "<<m_name<<std::endl;
Key = irr::KEY_KEY_CODES_COUNT;
return;
}
}
if (valid_kcode(Key)) {
m_name = KeyNames[Key];
} else {
m_name.resize(MB_CUR_MAX+1, '\0');
int written = wctomb(&m_name[0], Char);
assert (written >= 0 && "unexpected multibyte character");
if(written < 0){
std::string hexstr = hex_encode((const char*)&Char, sizeof(Char));
errorstream<<"KeyPress: Unexpected multibyte character "<<hexstr<<std::endl;
}
}
}

View File

@@ -33,7 +33,7 @@ class KeyPress
KeyPress();
KeyPress(const char *name);
KeyPress(const irr::SEvent::SKeyInput &in);
KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character=false);
bool operator==(const KeyPress &o) const
{

View File

@@ -38,7 +38,8 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef):
m_sneak_node_exists(false),
m_old_node_below(32767,32767,32767),
m_old_node_below_type("air"),
m_need_to_get_new_sneak_node(true)
m_need_to_get_new_sneak_node(true),
m_can_jump(false)
{
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
@@ -149,13 +150,16 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
f32 min_y = lwn_f.Y + 0.5*BS;
if(position.Y < min_y)
if(!is_climbing)
{
position.Y = min_y;
f32 min_y = lwn_f.Y + 0.5*BS;
if(position.Y < min_y)
{
position.Y = min_y;
if(m_speed.Y < 0)
m_speed.Y = 0;
if(m_speed.Y < 0)
m_speed.Y = 0;
}
}
}
@@ -188,7 +192,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
bool touching_ground_was = touching_ground;
touching_ground = result.touching_ground;
bool standing_on_unloaded = result.standing_on_unloaded;
//bool standing_on_unloaded = result.standing_on_unloaded;
/*
Check the nodes under the player to see from which node the
@@ -281,18 +285,25 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
/*
Report collisions
*/
bool bouncy_jump = false;
if(collision_info)
{
// Report fall collision
if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
{
CollisionInfo info;
info.t = COLLISION_FALL;
info.speed = m_speed.Y - old_speed.Y;
for(size_t i=0; i<result.collisions.size(); i++){
const CollisionInfo &info = result.collisions[i];
collision_info->push_back(info);
if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
info.bouncy)
bouncy_jump = true;
}
}
if(bouncy_jump && control.jump){
m_speed.Y += 6.5*BS;
touching_ground = false;
MtEvent *e = new SimpleTriggerEvent("PlayerJump");
m_gamedef->event()->put(e);
}
if(!touching_ground_was && touching_ground){
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_gamedef->event()->put(e);
@@ -314,6 +325,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
*/
m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS);
m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name;
/*
Check properties of the node on which the player is standing
*/
const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos()));
// Determine if jumping is possible
m_can_jump = touching_ground;
if(itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
}
void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
@@ -359,31 +379,70 @@ void LocalPlayer::applyControl(float dtime)
if(free_move && fast_move)
superspeed = true;
// Auxiliary button 1 (E)
if(control.aux1)
// Old descend control
if(g_settings->getBool("aux1_descends"))
{
if(free_move)
// Auxiliary button 1 (E)
if(control.aux1)
{
// In free movement mode, aux1 descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
if(free_move)
{
// In free movement mode, aux1 descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
else
speed.Y = -walkspeed_max;
setSpeed(speed);
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
}
else if(is_climbing)
}
// New minecraft-like descend control
else
{
// Auxiliary button 1 (E)
if(control.aux1)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
if(!free_move && !is_climbing)
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
}
}
else
if(control.sneak)
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
if(fast_move)
superspeed = true;
if(free_move)
{
// In free movement mode, sneak descends
v3f speed = getSpeed();
if(fast_move)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
setSpeed(speed);
}
else if(is_climbing)
{
v3f speed = getSpeed();
speed.Y = -3*BS;
setSpeed(speed);
}
}
}
@@ -420,7 +479,7 @@ void LocalPlayer::applyControl(float dtime)
speed.Y = walkspeed_max;
setSpeed(speed);
}
else if(touching_ground)
else if(m_can_jump)
{
/*
NOTE: The d value in move() affects jump height by

View File

@@ -101,6 +101,7 @@ class LocalPlayer : public Player
std::string m_old_node_below_type;
// Whether recalculation of the sneak node is needed
bool m_need_to_get_new_sneak_node;
bool m_can_jump;
};
#endif

View File

@@ -1468,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout,
MapBlock *block = (*i);
block->incrementUsageTimer(dtime);
if(block->getUsageTimer() > unload_timeout)
if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
{
v3s16 p = block->getPos();

View File

@@ -56,7 +56,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0)
m_usage_timer(0),
m_refcount(0)
{
data = NULL;
if(dummy == false)

View File

@@ -430,6 +430,22 @@ class MapBlock /*: public NodeContainer*/
{
return m_usage_timer;
}
/*
See m_refcount
*/
void refGrab()
{
m_refcount++;
}
void refDrop()
{
m_refcount--;
}
int refGet()
{
return m_refcount;
}
/*
Node Timers
@@ -566,6 +582,12 @@ class MapBlock /*: public NodeContainer*/
Map will unload the block when this reaches a timeout.
*/
float m_usage_timer;
/*
Reference count; currently used for determining if this block is in
the list of blocks to be drawn.
*/
int m_refcount;
};
inline bool blockpos_over_limit(v3s16 p)

View File

@@ -1297,7 +1297,9 @@ BiomeType get_biome(u64 seed, v2s16 p2d)
double d = noise2d_perlin(
0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
seed+9130, 3, 0.50);
if(d > 0.35)
if(d > 0.45)
return BT_DESERT;
if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 )
return BT_DESERT;
return BT_NORMAL;
};
@@ -1759,8 +1761,9 @@ void make_block(BlockMakeData *data)
vmanip.m_data[i] = airnode;
}
} else {
// Don't replace air or water or lava
if(vmanip.m_data[i].getContent() == CONTENT_AIR ||
// Don't replace air or water or lava or ignore
if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
vmanip.m_data[i].getContent() == CONTENT_AIR ||
vmanip.m_data[i].getContent() == c_water_source ||
vmanip.m_data[i].getContent() == c_lava_source)
continue;

View File

@@ -556,8 +556,6 @@ class CNodeDefManager: public IWritableNodeDefManager
f->solidness = 0;
} else {
f->solidness = 1;
if(f->alpha == 255)
f->solidness = 2;
f->backface_culling = false;
}
break;

View File

@@ -45,7 +45,8 @@ Player::Player(IGameDef *gamedef):
updateName("<not set>");
inventory.clear();
inventory.addList("main", PLAYER_INVENTORY_SIZE);
inventory.addList("craft", 9);
InventoryList *craft = inventory.addList("craft", 9);
craft->setWidth(3);
inventory.addList("craftpreview", 1);
inventory.addList("craftresult", 1);

View File

@@ -1847,6 +1847,20 @@ class InvRef
return 1;
}
// get_width(self, listname)
static int l_get_width(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const char *listname = luaL_checkstring(L, 2);
InventoryList *list = getlist(L, ref, listname);
if(list){
lua_pushinteger(L, list->getWidth());
} else {
lua_pushinteger(L, 0);
}
return 1;
}
// set_size(self, listname, size)
static int l_set_size(lua_State *L)
{
@@ -1869,6 +1883,23 @@ class InvRef
return 0;
}
// set_width(self, listname, size)
static int l_set_width(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const char *listname = luaL_checkstring(L, 2);
int newwidth = luaL_checknumber(L, 3);
Inventory *inv = getinv(L, ref);
InventoryList *list = inv->getList(listname);
if(list){
list->setWidth(newwidth);
} else {
return 0;
}
reportInventoryChange(L, ref);
return 0;
}
// get_stack(self, listname, i) -> itemstack
static int l_get_stack(lua_State *L)
{
@@ -2062,6 +2093,8 @@ const luaL_reg InvRef::methods[] = {
method(InvRef, is_empty),
method(InvRef, get_size),
method(InvRef, set_size),
method(InvRef, get_width),
method(InvRef, set_width),
method(InvRef, get_stack),
method(InvRef, set_stack),
method(InvRef, get_list),
@@ -3810,6 +3843,15 @@ class EnvRef
return 1;
}
// EnvRef:clear_objects()
// clear all objects in the environment
static int l_clear_objects(lua_State *L)
{
EnvRef *o = checkobject(L, 1);
o->m_env->clearAllObjects();
return 0;
}
public:
EnvRef(ServerEnvironment *env):
m_env(env)
@@ -3891,6 +3933,7 @@ const luaL_reg EnvRef::methods[] = {
method(EnvRef, find_node_near),
method(EnvRef, find_nodes_in_area),
method(EnvRef, get_perlin),
method(EnvRef, clear_objects),
{0,0}
};
@@ -4170,6 +4213,20 @@ static int l_log(lua_State *L)
return 0;
}
// request_shutdown()
static int l_request_shutdown(lua_State *L)
{
get_server(L)->requestShutdown();
return 0;
}
// get_server_status()
static int l_get_server_status(lua_State *L)
{
lua_pushstring(L, wide_to_narrow(get_server(L)->getStatusString()).c_str());
return 1;
}
// register_item_raw({lots of stuff})
static int l_register_item_raw(lua_State *L)
{
@@ -4560,6 +4617,56 @@ static int l_get_player_privs(lua_State *L)
return 1;
}
// get_ban_list()
static int l_get_ban_list(lua_State *L)
{
lua_pushstring(L, get_server(L)->getBanDescription("").c_str());
return 1;
}
// get_ban_description()
static int l_get_ban_description(lua_State *L)
{
const char * ip_or_name = luaL_checkstring(L, 1);
lua_pushstring(L, get_server(L)->getBanDescription(std::string(ip_or_name)).c_str());
return 1;
}
// ban_player()
static int l_ban_player(lua_State *L)
{
const char * name = luaL_checkstring(L, 1);
Player *player = get_env(L)->getPlayer(name);
if(player == NULL)
{
lua_pushboolean(L, false); // no such player
return 1;
}
try
{
Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id);
std::string ip_str = addr.serializeString();
get_server(L)->setIpBanned(ip_str, name);
}
catch(con::PeerNotFoundException) // unlikely
{
dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
lua_pushboolean(L, false); // error
return 1;
}
lua_pushboolean(L, true);
return 1;
}
// unban_player_or_ip()
static int l_unban_player_of_ip(lua_State *L)
{
const char * ip_or_name = luaL_checkstring(L, 1);
get_server(L)->unsetIpBanned(ip_or_name);
lua_pushboolean(L, true);
return 1;
}
// get_inventory(location)
static int l_get_inventory(lua_State *L)
{
@@ -4911,6 +5018,8 @@ static int l_rollback_revert_actions_by(lua_State *L)
static const struct luaL_Reg minetest_f [] = {
{"debug", l_debug},
{"log", l_log},
{"request_shutdown", l_request_shutdown},
{"get_server_status", l_get_server_status},
{"register_item_raw", l_register_item_raw},
{"register_alias_raw", l_register_alias_raw},
{"register_craft", l_register_craft},
@@ -4920,6 +5029,10 @@ static const struct luaL_Reg minetest_f [] = {
{"chat_send_all", l_chat_send_all},
{"chat_send_player", l_chat_send_player},
{"get_player_privs", l_get_player_privs},
{"get_ban_list", l_get_ban_list},
{"get_ban_description", l_get_ban_description},
{"ban_player", l_ban_player},
{"unban_player_or_ip", l_unban_player_of_ip},
{"get_inventory", l_get_inventory},
{"create_detached_inventory_raw", l_create_detached_inventory_raw},
{"get_dig_params", l_get_dig_params},
@@ -5510,6 +5623,8 @@ void scriptapi_on_player_receive_fields(lua_State *L,
// If that is nil or on error, return false and stack is unchanged
// If that is a function, returns true and pushes the
// function onto the stack
// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default
// is tried instead so unknown items can still be manipulated to some degree
static bool get_item_callback(lua_State *L,
const char *name, const char *callbackname)
{
@@ -5522,9 +5637,15 @@ static bool get_item_callback(lua_State *L,
// Should be a table
if(lua_type(L, -1) != LUA_TTABLE)
{
// Report error and clean up
errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
lua_pop(L, 1);
return false;
// Try minetest.nodedef_default instead
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "nodedef_default");
lua_remove(L, -2);
luaL_checktype(L, -1, LUA_TTABLE);
}
lua_getfield(L, -1, callbackname);
lua_remove(L, -2);

View File

@@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "voxel.h"
#include "config.h"
#include "servercommand.h"
#include "filesys.h"
#include "mapblock.h"
#include "serverobject.h"
@@ -443,9 +442,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
m_nearest_unsent_reset_timer += dtime;
if(m_nothing_to_send_pause_timer >= 0)
{
return;
}
Player *player = server->m_env->getPlayer(peer_id);
// This can happen sometimes; clients and players are not in perfect sync.
if(player == NULL)
return;
// Won't send anything if already sending
if(m_blocks_sending.size() >= g_settings->getU16
@@ -457,10 +459,6 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
//TimeTaker timer("RemoteClient::GetNextBlocks");
Player *player = server->m_env->getPlayer(peer_id);
assert(player != NULL);
v3f playerpos = player->getPosition();
v3f playerspeed = player->getSpeed();
v3f playerspeeddir(0,0,0);
@@ -2653,36 +2651,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Whether to send to other players
bool send_to_others = false;
// Parse commands
// Commands are implemented in Lua, so only catch invalid
// commands that were not "eaten" and send an error back
if(message[0] == L'/')
{
size_t strip_size = 1;
if (message[1] == L'#') // support old-style commans
++strip_size;
message = message.substr(strip_size);
WStrfnd f1(message);
f1.next(L" "); // Skip over /#whatever
std::wstring paramstring = f1.next(L"");
ServerCommandContext *ctx = new ServerCommandContext(
str_split(message, L' '),
paramstring,
this,
m_env,
player);
std::wstring reply(processServerCommand(ctx));
send_to_sender = ctx->flags & SEND_TO_SENDER;
send_to_others = ctx->flags & SEND_TO_OTHERS;
if (ctx->flags & SEND_NO_PREFIX)
line += reply;
message = message.substr(1);
send_to_sender = true;
if(message.length() == 0)
line += L"-!- Empty command";
else
line += L"Server: " + reply;
delete ctx;
line += L"-!- Invalid command: " + str_split(message, L' ')[0];
}
else
{
@@ -4449,9 +4427,10 @@ std::wstring Server::getStatusString()
// Uptime
os<<L", uptime="<<m_uptime.get();
// Information about clients
core::map<u16, RemoteClient*>::Iterator i;
bool first;
os<<L", clients={";
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
for(i = m_clients.getIterator(), first = true;
i.atEnd() == false; i++)
{
// Get client and check that it is valid
@@ -4466,7 +4445,11 @@ std::wstring Server::getStatusString()
if(player != NULL)
name = narrow_to_wide(player->getName());
// Add name to information string
os<<name<<L",";
if(!first)
os<<L",";
else
first = false;
os<<name;
}
os<<L"}";
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)

View File

@@ -1,191 +0,0 @@
/*
Part of Minetest-c55
Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "servercommand.h"
#include "settings.h"
#include "main.h" // For g_settings
#include "content_sao.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
void cmd_status(std::wostringstream &os,
ServerCommandContext *ctx)
{
os<<ctx->server->getStatusString();
}
void cmd_me(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(!ctx->server->checkPriv(ctx->player->getName(), "shout"))
{
os<<L"-!- You don't have permission to shout.";
return;
}
std::wstring name = narrow_to_wide(ctx->player->getName());
os << L"* " << name << L" " << ctx->paramstring;
ctx->flags |= SEND_TO_OTHERS | SEND_NO_PREFIX;
}
void cmd_time(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() != 2)
{
os<<L"-!- Missing parameter";
return;
}
if(!ctx->server->checkPriv(ctx->player->getName(), "settime"))
{
os<<L"-!- You don't have permission to do this.";
return;
}
u32 time = stoi(wide_to_narrow(ctx->parms[1]));
ctx->server->setTimeOfDay(time);
os<<L"-!- Time of day changed.";
actionstream<<ctx->player->getName()<<" sets time "
<<time<<std::endl;
}
void cmd_shutdown(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{
os<<L"-!- You don't have permission to do this.";
return;
}
actionstream<<ctx->player->getName()
<<" shuts down server"<<std::endl;
ctx->server->requestShutdown();
os<<L"*** Server shutting down (operator request).";
ctx->flags |= SEND_TO_OTHERS;
}
void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
{
if(!ctx->server->checkPriv(ctx->player->getName(), "ban"))
{
os<<L"-!- You don't have permission to do this.";
return;
}
if(ctx->parms.size() < 2)
{
std::string desc = ctx->server->getBanDescription("");
os<<L"-!- Ban list: "<<narrow_to_wide(desc);
return;
}
if(ctx->parms[0] == L"ban")
{
Player *player = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(player == NULL)
{
os<<L"-!- No such player";
return;
}
try{
Address address = ctx->server->getPeerAddress(player->peer_id);
std::string ip_string = address.serializeString();
ctx->server->setIpBanned(ip_string, player->getName());
os<<L"-!- Banned "<<narrow_to_wide(ip_string)<<L"|"
<<narrow_to_wide(player->getName());
actionstream<<ctx->player->getName()<<" bans "
<<player->getName()<<" / "<<ip_string<<std::endl;
} catch(con::PeerNotFoundException){
dstream<<__FUNCTION_NAME<<": peer was not found"<<std::endl;
}
}
else
{
std::string ip_or_name = wide_to_narrow(ctx->parms[1]);
std::string desc = ctx->server->getBanDescription(ip_or_name);
ctx->server->unsetIpBanned(ip_or_name);
os<<L"-!- Unbanned "<<narrow_to_wide(desc);
actionstream<<ctx->player->getName()<<" unbans "
<<ip_or_name<<std::endl;
}
}
void cmd_clearobjects(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{
os<<L"-!- You don't have permission to do this.";
return;
}
actionstream<<ctx->player->getName()
<<" clears all objects"<<std::endl;
{
std::wstring msg;
msg += L"Clearing all objects. This may take long.";
msg += L" You may experience a timeout. (by ";
msg += narrow_to_wide(ctx->player->getName());
msg += L")";
ctx->server->notifyPlayers(msg);
}
ctx->env->clearAllObjects();
actionstream<<"object clearing done"<<std::endl;
os<<L"*** Cleared all objects.";
ctx->flags |= SEND_TO_OTHERS;
}
std::wstring processServerCommand(ServerCommandContext *ctx)
{
std::wostringstream os(std::ios_base::binary);
ctx->flags = SEND_TO_SENDER; // Default, unless we change it.
if(ctx->parms.size() == 0)
os<<L"-!- Empty command";
else if(ctx->parms[0] == L"status")
cmd_status(os, ctx);
else if(ctx->parms[0] == L"time")
cmd_time(os, ctx);
else if(ctx->parms[0] == L"shutdown")
cmd_shutdown(os, ctx);
else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
cmd_banunban(os, ctx);
else if(ctx->parms[0] == L"me")
cmd_me(os, ctx);
else if(ctx->parms[0] == L"clearobjects")
cmd_clearobjects(os, ctx);
else
os<<L"-!- Invalid command: " + ctx->parms[0];
return os.str();
}

View File

@@ -1,62 +0,0 @@
/*
Part of Minetest-c55
Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef SERVERCOMMAND_HEADER
#define SERVERCOMMAND_HEADER
#include <vector>
#include <sstream>
#include "irrlichttypes.h"
#include "player.h"
#include "server.h"
#define SEND_TO_SENDER (1<<0)
#define SEND_TO_OTHERS (1<<1)
#define SEND_NO_PREFIX (1<<2)
struct ServerCommandContext
{
std::vector<std::wstring> parms;
std::wstring paramstring;
Server* server;
ServerEnvironment *env;
Player* player;
u32 flags;
ServerCommandContext(
std::vector<std::wstring> parms,
std::wstring paramstring,
Server* server,
ServerEnvironment *env,
Player* player)
: parms(parms), paramstring(paramstring),
server(server), env(env), player(player)
{
}
};
// Process a command sent from a client. The environment and connection
// should be locked when this is called.
// Returns a response message, to be dealt with according to the flags set
// in the context.
std::wstring processServerCommand(ServerCommandContext *ctx);
#endif

View File

@@ -30,7 +30,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#elif defined(__APPLE__)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <OpenAL/alext.h>
//#include <OpenAL/alext.h>
#else
#include <AL/al.h>
#include <AL/alc.h>

View File

@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <set>
#include <vector>
#define WORLDNAME_BLACKLISTED_CHARS "/\\"
struct SubgameSpec
{
std::string id; // "" = game does not exist

View File

@@ -698,6 +698,7 @@ struct TestInventory: public TestBase
{
std::string serialized_inventory =
"List 0 32\n"
"Width 3\n"
"Empty\n"
"Empty\n"
"Empty\n"
@@ -735,6 +736,7 @@ struct TestInventory: public TestBase
std::string serialized_inventory_2 =
"List main 32\n"
"Width 5\n"
"Empty\n"
"Empty\n"
"Empty\n"
@@ -778,6 +780,8 @@ struct TestInventory: public TestBase
inv.getList("0")->setName("main");
UASSERT(!inv.getList("0"));
UASSERT(inv.getList("main"));
UASSERT(inv.getList("main")->getWidth() == 3);
inv.getList("main")->setWidth(5);
std::ostringstream inv_os(std::ios::binary);
inv.serialize(inv_os);
UASSERT(inv_os.str() == serialized_inventory_2);

View File

@@ -1200,10 +1200,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
image->copyToWithAlpha(baseimg, pos_to,
/*image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
NULL);
NULL);*/
blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
// Drop image
image->drop();
}
@@ -1344,7 +1345,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
u32 h0 = stoi(sf.next(":"));
infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0);
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
if(baseimg == NULL)
{
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
baseimg->fill(video::SColor(0,0,0,0));
}
while(sf.atend() == false)
{
u32 x = stoi(sf.next(","));
@@ -1364,10 +1369,11 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
driver->createImage(video::ECF_A8R8G8B8, dim);
img->copyTo(img2);
img->drop();
img2->copyToWithAlpha(baseimg, pos_base,
/*img2->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255),
NULL);
NULL);*/
blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
img2->drop();
}
else

View File

@@ -242,6 +242,29 @@ inline bool string_allowed(const std::string &s, const std::string &allowed_char
return true;
}
/*
Checks if a string contains no blacklisted characters (opposite
function of string_allowed())
*/
inline bool string_allowed_blacklist(const std::string & s, const std::string & blacklisted_chars)
{
for(unsigned int i = 0; i < s.length(); i++)
{
bool invalid = false;
for(unsigned int j = 0; j < blacklisted_chars.length(); j++)
{
if(s[i] == blacklisted_chars[j])
{
invalid = true;
break;
}
}
if(invalid)
return false;
}
return true;
}
/*
Forcefully wraps string into rows using \n
(no word wrap, used for showing paths in gui)