This commit is contained in:
Calinou 2013-01-06 11:53:47 +01:00
commit ae898a3b9e
38 changed files with 1528 additions and 340 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 # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 4) set(VERSION_MINOR 4)
set(VERSION_PATCH 4) set(VERSION_PATCH 4-d1)
if(VERSION_EXTRA) if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif() endif()

View File

@ -192,11 +192,15 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
-- Add node and update -- Add node and update
minetest.env:add_node(place_to, newnode) minetest.env:add_node(place_to, newnode)
local take_item = true
-- Run callback -- Run callback
if def.after_place_node then if def.after_place_node then
-- Copy place_to because callback can modify it -- Copy place_to because callback can modify it
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
def.after_place_node(place_to_copy, placer) if def.after_place_node(place_to_copy, placer, itemstack) then
take_item = false
end
end end
-- Run script hook -- Run script hook
@ -206,10 +210,14 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2} local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2} local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
callback(place_to_copy, newnode_copy, placer, oldnode_copy) if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack) then
take_item = false
end
end end
if take_item then
itemstack:take_item() itemstack:take_item()
end
return itemstack return itemstack
end end
@ -223,9 +231,19 @@ function minetest.item_place_object(itemstack, placer, pointed_thing)
end end
function minetest.item_place(itemstack, placer, pointed_thing) function minetest.item_place(itemstack, placer, pointed_thing)
-- Call on_rightclick if the pointed node defines it
if pointed_thing.type == "node" then
local n = minetest.env:get_node(pointed_thing.under)
local nn = n.name
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then
minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, placer)
return
end
end
if itemstack:get_definition().type == "node" then if itemstack:get_definition().type == "node" then
return minetest.item_place_node(itemstack, placer, pointed_thing) return minetest.item_place_node(itemstack, placer, pointed_thing)
else elseif itemstack:get_definition().type ~= "none" then
return minetest.item_place_object(itemstack, placer, pointed_thing) return minetest.item_place_object(itemstack, placer, pointed_thing)
end end
end end
@ -367,6 +385,7 @@ minetest.nodedef_default = {
can_dig = nil, can_dig = nil,
on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
on_rightclick = nil,
on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
on_receive_fields = nil, on_receive_fields = nil,
@ -456,7 +475,7 @@ minetest.noneitemdef_default = { -- This is used for the hand and unknown items
tool_capabilities = nil, tool_capabilities = nil,
-- Interaction callbacks -- Interaction callbacks
on_place = nil, on_place = redef_wrapper(minetest, 'item_place'),
on_drop = nil, on_drop = nil,
on_use = nil, on_use = nil,
} }

View File

@ -84,8 +84,9 @@ minetest.register_entity("__builtin:item", {
local p = self.object:getpos() local p = self.object:getpos()
p.y = p.y - 0.3 p.y = p.y - 0.3
local nn = minetest.env:get_node(p).name local nn = minetest.env:get_node(p).name
-- If node is not registered or node is walkably solid -- If node is not registered or node is walkably solid and resting on nodebox
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable then local v = self.object:getvelocity()
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and v.y == 0 then
if self.physical_state then if self.physical_state then
self.object:setvelocity({x=0,y=0,z=0}) self.object:setvelocity({x=0,y=0,z=0})
self.object:setacceleration({x=0, y=0, z=0}) self.object:setacceleration({x=0, y=0, z=0})

View File

@ -44,5 +44,9 @@ minetest.register_privilege("fast", {
description = "Can walk fast using the fast_move mode", description = "Can walk fast using the fast_move mode",
give_to_singleplayer = false, give_to_singleplayer = false,
}) })
minetest.register_privilege("noclip", {
description = "Can fly through walls",
give_to_singleplayer = false,
})
minetest.register_privilege("rollback", "Can use the rollback functionality") minetest.register_privilege("rollback", "Can use the rollback functionality")

View File

@ -0,0 +1,42 @@
# - Find curl
# Find the native CURL headers and libraries.
#
# CURL_INCLUDE_DIR - where to find curl/curl.h, etc.
# CURL_LIBRARY - List of libraries when using curl.
# CURL_FOUND - True if curl found.
if( UNIX )
FIND_PATH(CURL_INCLUDE_DIR NAMES curl.h
PATHS
/usr/local/include/curl
/usr/include/curl
)
FIND_LIBRARY(CURL_LIBRARY NAMES curl
PATHS
/usr/local/lib
/usr/lib
)
else( UNIX )
FIND_PATH(CURL_INCLUDE_DIR NAMES curl/curl.h) # Look for the header file.
FIND_LIBRARY(CURL_LIBRARY NAMES curl) # Look for the library.
INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE if
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CURL DEFAULT_MSG CURL_LIBRARY CURL_INCLUDE_DIR) # all listed variables are TRUE
endif( UNIX )
if( WIN32 )
if( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL ) # libcurl.dll is required on Windows
SET(CURL_FOUND TRUE)
else( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL )
SET(CURL_FOUND FALSE)
endif( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL )
else ( WIN32 )
if( CURL_LIBRARY AND CURL_INCLUDE_DIR )
SET(CURL_FOUND TRUE)
else( CURL_LIBRARY AND CURL_INCLUDE_DIR )
SET(CURL_FOUND FALSE)
endif( CURL_LIBRARY AND CURL_INCLUDE_DIR )
endif ( WIN32 )
MESSAGE(STATUS "CURL_INCLUDE_DIR = ${CURL_INCLUDE_DIR}")
MESSAGE(STATUS "CURL_LIBRARY = ${CURL_LIBRARY}")

View File

@ -725,6 +725,9 @@ field[<name>;<label>;<default>]
^ must be used without a size[] element ^ must be used without a size[] element
^ a 'Proceed' button will be added automatically ^ a 'Proceed' button will be added automatically
textarea[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ same as fields above, but with multi-line input
label[<X>,<Y>;<label>] label[<X>,<Y>;<label>]
^ x and y work as per field ^ x and y work as per field
^ label is the text on the label ^ label is the text on the label
@ -810,8 +813,9 @@ minetest.register_on_shutdown(func())
^ WARNING: If the server terminates abnormally (i.e. crashes), the registered ^ WARNING: If the server terminates abnormally (i.e. crashes), the registered
callbacks WILL LIKELY NOT BE RUN. Data should be saved at callbacks WILL LIKELY NOT BE RUN. Data should be saved at
semi-frequent intervals as well as on server shutdown. semi-frequent intervals as well as on server shutdown.
minetest.register_on_placenode(func(pos, newnode, placer, oldnode)) minetest.register_on_placenode(func(pos, newnode, placer, oldnode, itemstack))
^ Called when a node has been placed ^ Called when a node has been placed
^ If return true no item is taken from itemstack
^ Deprecated: Use on_construct or after_place_node in node definition instead ^ Deprecated: Use on_construct or after_place_node in node definition instead
minetest.register_on_dignode(func(pos, oldnode, digger)) minetest.register_on_dignode(func(pos, oldnode, digger))
^ Called when a node has been dug. ^ Called when a node has been dug.
@ -887,6 +891,9 @@ minetest.get_inventory(location) -> InvRef
minetest.create_detached_inventory(name, callbacks) -> InvRef minetest.create_detached_inventory(name, callbacks) -> InvRef
^ callbacks: See "Detached inventory callbacks" ^ callbacks: See "Detached inventory callbacks"
^ Creates a detached inventory. If it already exists, it is cleared. ^ Creates a detached inventory. If it already exists, it is cleared.
minetest.show_formspec(playername, formspec)
^ playername: name of player to show formspec
^ formspec: formspec to display
Item handling: Item handling:
minetest.inventorycube(img1, img2, img3) minetest.inventorycube(img1, img2, img3)
@ -938,6 +945,8 @@ minetest.item_place_object(itemstack, placer, pointed_thing)
^ Place item as-is ^ Place item as-is
minetest.item_place(itemstack, placer, pointed_thing) minetest.item_place(itemstack, placer, pointed_thing)
^ Use one of the above based on what the item is. ^ Use one of the above based on what the item is.
^ Calls on_rightclick of pointed_thing.under if defined instead
^ Note: is not called when wielded item overrides on_place
minetest.item_drop(itemstack, dropper, pos) minetest.item_drop(itemstack, dropper, pos)
^ Drop the item ^ Drop the item
minetest.item_eat(hp_change, replace_with_item) minetest.item_eat(hp_change, replace_with_item)
@ -1062,6 +1071,60 @@ methods:
^ Return world-specific perlin noise (int(worldseed)+seeddiff) ^ Return world-specific perlin noise (int(worldseed)+seeddiff)
- clear_objects() - clear_objects()
^ clear all objects in the environments ^ clear all objects in the environments
- spawn_tree (pos, {treedef})
^ spawns L-System tree at given pos with definition in treedef table
treedef={
axiom, - string initial tree axiom
rules_a, - string rules set A
rules_b, - string rules set B
rules_c, - string rules set C
rules_d, - string rules set D
trunk, - string trunk node name
leaves, - string leaves node name
angle, - num angle in deg
iterations, - num max # of iterations, usually 2 -5
random_level, - num factor to lower nr of iterations, usually 0 - 3
thin_trunks, - boolean true -> use thin trunks
fruit_tree, - boolean true -> is a fruit tree
fruit - string fruit node name
}
Key for Special L-System Symbols used in Axioms
G - move forward one unit with the pin down
F - move forward one unit with the pin up
A - replace with rules set A
B - replace with rules set B
C - replace with rules set C
D - replace with rules set D
a - replace with rules set A, chance 90%
b - replace with rules set B, chance 80%
c - replace with rules set C, chance 70%
d - replace with rules set D, chance 60%
+ - yaw the turtle right by angle parameter
- - yaw the turtle left by angle parameter
& - pitch the turtle down by angle parameter
^ - pitch the turtle up by angle parameter
/ - roll the turtle to the right by angle parameter
* - roll the turtle to the left by angle parameter
[ - save in stack current state info
] - recover from stack state info
Example usage: spawn small apple tree
apple_tree={
axiom="FFFFFAFFBF",
rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]",
rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]",
trunk="default:tree",
leaves="default:leaves",
angle=30,
iterations=2,
random_level=0,
thin_trunks=true,
fruit_tree=true,
fruit="default:apple"
}
minetest.env:spawn_tree(pos,apple_tree)
Deprecated: Deprecated:
- add_rat(pos): Add C++ rat object (no-op) - add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op) - add_firefly(pos): Add C++ firefly object (no-op)
@ -1173,6 +1236,8 @@ methods:
can be fully taken from the list can be fully taken from the list
remove_item(listname, stack): take as many items as specified from the list, remove_item(listname, stack): take as many items as specified from the list,
returns the items that were actually removed (as an ItemStack) returns the items that were actually removed (as an ItemStack)
- get_location() -> location compatible to minetest.get_inventory(location)
-> {type="undefined"} in case location is not known
ItemStack: A stack of items. ItemStack: A stack of items.
- Can be created via ItemStack(itemstack or itemstring or table or nil) - Can be created via ItemStack(itemstack or itemstring or table or nil)
@ -1405,9 +1470,10 @@ Node definition (register_node)
^ Node destructor; always called after removing node ^ Node destructor; always called after removing node
^ default: nil ^ default: nil
after_place_node = func(pos, placer), after_place_node = func(pos, placer, itemstack),
^ Called after constructing node when node was placed using ^ Called after constructing node when node was placed using
minetest.item_place_node / minetest.env:place_node minetest.item_place_node / minetest.env:place_node
^ If return true no item is taken from itemstack
^ default: nil ^ default: nil
after_dig_node = func(pos, oldnode, oldmetadata, digger), after_dig_node = func(pos, oldnode, oldmetadata, digger),
^ oldmetadata is in table format ^ oldmetadata is in table format
@ -1421,6 +1487,8 @@ Node definition (register_node)
on_punch = func(pos, node, puncher), on_punch = func(pos, node, puncher),
^ default: minetest.node_punch ^ default: minetest.node_punch
^ By default: does nothing ^ By default: does nothing
on_rightclick = func(pos, node, clicker),
^ default: nil
on_dig = func(pos, node, digger), on_dig = func(pos, node, digger),
^ default: minetest.node_dig ^ default: minetest.node_dig
^ By default: checks privileges, wears out tool and removes node ^ By default: checks privileges, wears out tool and removes node

View File

@ -41,7 +41,7 @@ Show allowed options
Set logfile path (debug.txt) Set logfile path (debug.txt)
.TP .TP
\-\-map\-dir <value> \-\-map\-dir <value>
Same as --world (deprecated) Same as \-\-world (deprecated)
.TP .TP
\-\-name <value> \-\-name <value>
Set player name Set player name

View File

@ -35,7 +35,7 @@ Show allowed options
Set logfile path (debug.txt) Set logfile path (debug.txt)
.TP .TP
\-\-map\-dir <value> \-\-map\-dir <value>
Same as --world (deprecated) Same as \-\-world (deprecated)
.TP .TP
\-\-port <value> \-\-port <value>
Set network port (UDP) to use Set network port (UDP) to use

View File

@ -51,6 +51,8 @@
#keymap_screenshot = KEY_F12 #keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending # If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false #aux1_descends = false
# Doubletaping the jump key toogles fly mode
#doubletap_jump = false
# Some (temporary) keys for debugging # Some (temporary) keys for debugging
#keymap_print_debug_stacks = KEY_KEY_P #keymap_print_debug_stacks = KEY_KEY_P
#keymap_quicktune_prev = KEY_HOME #keymap_quicktune_prev = KEY_HOME
@ -124,6 +126,12 @@
#console_color = (0,0,0) #console_color = (0,0,0)
# In-game chat console background alpha (opaqueness, between 0 and 255) # In-game chat console background alpha (opaqueness, between 0 and 255)
#console_alpha = 200 #console_alpha = 200
# Selection box border color (R,G,B)
#selectionbox_color = (0,0,0)
# Crosshair color (R,G,B)
#crosshair_color = (255,255,255)
# Cross alpha (opaqueness, between 0 and 255)
#crosshair_alpha = 255
# Sound settings # Sound settings
#enable_sound = true #enable_sound = true
#sound_volume = 0.7 #sound_volume = 0.7
@ -140,6 +148,13 @@
# (1: low level shaders; not implemented) # (1: low level shaders; not implemented)
# 2: enable high level shaders # 2: enable high level shaders
#enable_shaders = 2 #enable_shaders = 2
# The time in seconds it takes between repeated
# right clicks when holding the right mouse button
#repeat_rightclick_time = 0.25
# will only work for servers which use remote_media setting
# and only for clients compiled with cURL
#media_fetch_threads = 8
# #
# Server stuff # Server stuff
@ -217,3 +232,4 @@
#congestion_control_aim_rtt = 0.2 #congestion_control_aim_rtt = 0.2
#congestion_control_max_rate = 400 #congestion_control_max_rate = 400
#congestion_control_min_rate = 10 #congestion_control_min_rate = 10
#remote_media =

View File

@ -6,6 +6,21 @@ mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY) mark_as_advanced(JTHREAD_INCLUDE_DIR JTHREAD_LIBRARY)
mark_as_advanced(SQLITE3_INCLUDE_DIR SQLITE3_LIBRARY) mark_as_advanced(SQLITE3_INCLUDE_DIR SQLITE3_LIBRARY)
option(ENABLE_CURL "Enable cURL support for fetching media" 1)
if (NOT ENABLE_CURL)
mark_as_advanced(CLEAR CURL_LIBRARY CURL_INCLUDE_DIR)
endif(NOT ENABLE_CURL)
if( ENABLE_CURL )
find_package(CURL)
endif( ENABLE_CURL )
set(USE_CURL 0)
if (CURL_FOUND AND ENABLE_CURL)
message(STATUS "cURL support enabled")
set(USE_CURL 1)
endif(CURL_FOUND AND ENABLE_CURL)
# user-visible option to enable/disable gettext usage # user-visible option to enable/disable gettext usage
OPTION(ENABLE_GETTEXT "Use GetText for internationalization" 0) OPTION(ENABLE_GETTEXT "Use GetText for internationalization" 0)
@ -173,6 +188,7 @@ set(common_SRCS
log.cpp log.cpp
content_sao.cpp content_sao.cpp
mapgen.cpp mapgen.cpp
treegen.cpp
content_nodemeta.cpp content_nodemeta.cpp
content_mapnode.cpp content_mapnode.cpp
collision.cpp collision.cpp
@ -307,6 +323,16 @@ if(BUILD_CLIENT)
${PLATFORM_LIBS} ${PLATFORM_LIBS}
${CLIENT_PLATFORM_LIBS} ${CLIENT_PLATFORM_LIBS}
) )
if(USE_CURL)
target_link_libraries(
${PROJECT_NAME}
${CURL_LIBRARY}
)
include_directories(
${CURL_INCLUDE_DIR}
)
endif(USE_CURL)
endif(BUILD_CLIENT) endif(BUILD_CLIENT)
if(BUILD_SERVER) if(BUILD_SERVER)
@ -413,6 +439,9 @@ if(WIN32)
if(VORBISFILE_DLL) if(VORBISFILE_DLL)
install(FILES ${VORBISFILE_DLL} DESTINATION ${BINDIR}) install(FILES ${VORBISFILE_DLL} DESTINATION ${BINDIR})
endif() endif()
if(CURL_DLL)
install(FILES ${CURL_DLL} DESTINATION ${BINDIR})
endif()
endif() endif()
endif() endif()

View File

@ -233,9 +233,12 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize,
m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0)); m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
m_playernode->updateAbsolutePosition(); m_playernode->updateAbsolutePosition();
//Get camera tilt timer (hurt animation)
float cameratilt = fabs(fabs(player->hurt_tilt_timer-0.75)-0.75);
// Set head node transformation // Set head node transformation
m_headnode->setPosition(player->getEyeOffset()); m_headnode->setPosition(player->getEyeOffset()+v3f(0,cameratilt*-player->hurt_tilt_strength,0));
m_headnode->setRotation(v3f(player->getPitch(), 0, 0)); m_headnode->setRotation(v3f(player->getPitch(), 0, cameratilt*player->hurt_tilt_strength));
m_headnode->updateAbsolutePosition(); m_headnode->updateAbsolutePosition();
// Compute relative camera position and target // Compute relative camera position and target

View File

@ -44,21 +44,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "hex.h" #include "hex.h"
#include "IMeshCache.h" #include "IMeshCache.h"
#include "util/serialize.h" #include "util/serialize.h"
#include "config.h"
#if USE_CURL
#include <curl/curl.h>
#endif
static std::string getMediaCacheDir() static std::string getMediaCacheDir()
{ {
return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media"; return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
} }
struct MediaRequest
{
std::string name;
MediaRequest(const std::string &name_=""):
name(name_)
{}
};
/* /*
QueuedMeshUpdate QueuedMeshUpdate
*/ */
@ -223,6 +219,46 @@ void * MeshUpdateThread::Thread()
return NULL; return NULL;
} }
void * MediaFetchThread::Thread()
{
ThreadStarted();
log_register_thread("MediaFetchThread");
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
#if USE_CURL
CURL *curl;
CURLcode res;
for (core::list<MediaRequest>::Iterator i = m_file_requests.begin();
i != m_file_requests.end(); i++) {
curl = curl_easy_init();
assert(curl);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
std::ostringstream stream;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
std::string data = stream.str();
m_file_data.push_back(make_pair(i->name, data));
} else {
m_failed.push_back(*i);
infostream << "cURL request failed for " << i->name << std::endl;
}
curl_easy_cleanup(curl);
}
#endif
END_DEBUG_EXCEPTION_HANDLER(errorstream)
return NULL;
}
Client::Client( Client::Client(
IrrlichtDevice *device, IrrlichtDevice *device,
const char *playername, const char *playername,
@ -263,8 +299,9 @@ Client::Client(
m_password(password), m_password(password),
m_access_denied(false), m_access_denied(false),
m_media_cache(getMediaCacheDir()), m_media_cache(getMediaCacheDir()),
m_media_receive_progress(0), m_media_receive_started(false),
m_media_received(false), m_media_count(0),
m_media_received_count(0),
m_itemdef_received(false), m_itemdef_received(false),
m_nodedef_received(false), m_nodedef_received(false),
m_time_of_day_set(false), m_time_of_day_set(false),
@ -296,6 +333,9 @@ Client::Client(
m_env.addPlayer(player); m_env.addPlayer(player);
} }
for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
m_media_fetch_threads.push_back(new MediaFetchThread(this));
} }
Client::~Client() Client::~Client()
@ -319,6 +359,10 @@ Client::~Client()
delete i->second; delete i->second;
} }
} }
for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
i != m_media_fetch_threads.end(); i++)
delete *i;
} }
void Client::connect(Address address) void Client::connect(Address address)
@ -730,6 +774,62 @@ void Client::step(float dtime)
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
} }
/*
Load fetched media
*/
if (m_media_receive_started) {
bool all_stopped = true;
for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
thread != m_media_fetch_threads.end(); thread++) {
all_stopped &= !(*thread)->IsRunning();
while ((*thread)->m_file_data.size() > 0) {
std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
++m_media_received_count;
bool success = loadMedia(out.second, out.first);
if(success){
verbosestream<<"Client: Loaded received media: "
<<"\""<<out.first<<"\". Caching."<<std::endl;
} else{
infostream<<"Client: Failed to load received media: "
<<"\""<<out.first<<"\". Not caching."<<std::endl;
continue;
}
bool did = fs::CreateAllDirs(getMediaCacheDir());
if(!did){
errorstream<<"Could not create media cache directory"
<<std::endl;
}
{
core::map<std::string, std::string>::Node *n;
n = m_media_name_sha1_map.find(out.first);
if(n == NULL)
errorstream<<"The server sent a file that has not "
<<"been announced."<<std::endl;
else
m_media_cache.update_sha1(out.second);
}
}
}
if (all_stopped) {
core::list<MediaRequest> fetch_failed;
for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
thread != m_media_fetch_threads.end(); thread++) {
for (core::list<MediaRequest>::Iterator request = (*thread)->m_failed.begin();
request != (*thread)->m_failed.end(); request++)
fetch_failed.push_back(*request);
(*thread)->m_failed.clear();
}
if (fetch_failed.size() > 0) {
infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
<< "Requesting them the usual way." << std::endl;
request_media(fetch_failed);
}
}
}
/* /*
If the server didn't update the inventory in a while, revert If the server didn't update the inventory in a while, revert
the local inventory (so the player notices the lag problem the local inventory (so the player notices the lag problem
@ -907,6 +1007,34 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
<<"(timeout="<<timeout<<")"<<std::endl; <<"(timeout="<<timeout<<")"<<std::endl;
} }
/*
u16 command
u16 number of files requested
for each file {
u16 length of name
string name
}
*/
void Client::request_media(const core::list<MediaRequest> &file_requests)
{
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_REQUEST_MEDIA);
writeU16(os, file_requests.size());
for(core::list<MediaRequest>::ConstIterator i = file_requests.begin();
i != file_requests.end(); i++) {
os<<serializeString(i->name);
}
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
Send(0, data, true);
infostream<<"Client: Sending media request list to server ("
<<file_requests.size()<<" files)"<<std::endl;
}
void Client::ReceiveAll() void Client::ReceiveAll()
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
@ -1514,37 +1642,52 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
file_requests.push_back(MediaRequest(name)); file_requests.push_back(MediaRequest(name));
} }
ClientEvent event; std::string remote_media = "";
event.type = CE_TEXTURES_UPDATED; try {
m_client_event_queue.push_back(event); remote_media = deSerializeString(is);
}
/* catch(SerializationError) {
u16 command // not supported by server or turned off
u16 number of files requested
for each file {
u16 length of name
string name
} }
*/
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_REQUEST_MEDIA);
writeU16(os, file_requests.size());
m_media_count = file_requests.size();
m_media_receive_started = true;
if (remote_media == "" || !USE_CURL) {
request_media(file_requests);
} else {
#if USE_CURL
core::list<MediaFetchThread*>::Iterator cur = m_media_fetch_threads.begin();
for(core::list<MediaRequest>::Iterator i = file_requests.begin(); for(core::list<MediaRequest>::Iterator i = file_requests.begin();
i != file_requests.end(); i++) { i != file_requests.end(); i++) {
os<<serializeString(i->name); (*cur)->m_file_requests.push_back(*i);
cur++;
if (cur == m_media_fetch_threads.end())
cur = m_media_fetch_threads.begin();
} }
for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
i != m_media_fetch_threads.end(); i++) {
(*i)->m_remote_url = remote_media;
(*i)->Start();
}
#endif
// Make data buffer // notify server we received everything
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_RECEIVED_MEDIA);
std::string s = os.str(); std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size()); SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable // Send as reliable
Send(0, data, true); Send(0, data, true);
infostream<<"Client: Sending media request list to server (" }
<<file_requests.size()<<" files)"<<std::endl; ClientEvent event;
event.type = CE_TEXTURES_UPDATED;
m_client_event_queue.push_back(event);
} }
else if(command == TOCLIENT_MEDIA) else if(command == TOCLIENT_MEDIA)
{ {
if (m_media_count == 0)
return;
std::string datastring((char*)&data[2], datasize-2); std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary); std::istringstream is(datastring, std::ios_base::binary);
@ -1566,17 +1709,12 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
*/ */
int num_bunches = readU16(is); int num_bunches = readU16(is);
int bunch_i = readU16(is); int bunch_i = readU16(is);
if(num_bunches >= 2)
m_media_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
else
m_media_receive_progress = 1.0;
if(bunch_i == num_bunches - 1)
m_media_received = true;
int num_files = readU32(is); int num_files = readU32(is);
infostream<<"Client: Received files: bunch "<<bunch_i<<"/" infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
<<num_bunches<<" files="<<num_files <<num_bunches<<" files="<<num_files
<<" size="<<datasize<<std::endl; <<" size="<<datasize<<std::endl;
for(int i=0; i<num_files; i++){ for(int i=0; i<num_files; i++){
m_media_received_count++;
std::string name = deSerializeString(is); std::string name = deSerializeString(is);
std::string data = deSerializeLongString(is); std::string data = deSerializeLongString(is);
@ -1762,6 +1900,20 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
} }
inv->deSerialize(is); inv->deSerialize(is);
} }
else if(command == TOCLIENT_SHOW_FORMSPEC)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
std::string formspec = deSerializeLongString(is);
ClientEvent event;
event.type = CE_SHOW_FORMSPEC;
// pointer is required as event is a struct only!
// adding a std:string to a struct isn't possible
event.show_formspec.formspec = new std::string(formspec);
m_client_event_queue.push_back(event);
}
else else
{ {
infostream<<"Client: Ignoring unknown command " infostream<<"Client: Ignoring unknown command "
@ -2458,7 +2610,7 @@ void Client::afterContentReceived()
infostream<<"Client::afterContentReceived() started"<<std::endl; infostream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received); assert(m_itemdef_received);
assert(m_nodedef_received); assert(m_nodedef_received);
assert(m_media_received); assert(texturesReceived());
// remove the information about which checksum each texture // remove the information about which checksum each texture
// ought to have // ought to have

View File

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h" #include "filesys.h"
#include "filecache.h" #include "filecache.h"
#include "localplayer.h" #include "localplayer.h"
#include "server.h"
#include "util/pointedthing.h" #include "util/pointedthing.h"
struct MeshMakeData; struct MeshMakeData;
@ -129,13 +130,32 @@ public:
IGameDef *m_gamedef; IGameDef *m_gamedef;
}; };
class MediaFetchThread : public SimpleThread
{
public:
MediaFetchThread(IGameDef *gamedef):
m_gamedef(gamedef)
{
}
void * Thread();
core::list<MediaRequest> m_file_requests;
MutexedQueue<std::pair<std::string, std::string> > m_file_data;
core::list<MediaRequest> m_failed;
std::string m_remote_url;
IGameDef *m_gamedef;
};
enum ClientEventType enum ClientEventType
{ {
CE_NONE, CE_NONE,
CE_PLAYER_DAMAGE, CE_PLAYER_DAMAGE,
CE_PLAYER_FORCE_MOVE, CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN, CE_DEATHSCREEN,
CE_TEXTURES_UPDATED CE_TEXTURES_UPDATED,
CE_SHOW_FORMSPEC
}; };
struct ClientEvent struct ClientEvent
@ -157,6 +177,9 @@ struct ClientEvent
f32 camera_point_target_y; f32 camera_point_target_y;
f32 camera_point_target_z; f32 camera_point_target_z;
} deathscreen; } deathscreen;
struct{
std::string* formspec;
} show_formspec;
struct{ struct{
} textures_updated; } textures_updated;
}; };
@ -289,10 +312,13 @@ public:
{ return m_access_denied_reason; } { return m_access_denied_reason; }
float mediaReceiveProgress() float mediaReceiveProgress()
{ return m_media_receive_progress; } {
if (!m_media_receive_started) return 0;
return 1.0 * m_media_received_count / m_media_count;
}
bool texturesReceived() bool texturesReceived()
{ return m_media_received; } { return m_media_receive_started && m_media_received_count == m_media_count; }
bool itemdefReceived() bool itemdefReceived()
{ return m_itemdef_received; } { return m_itemdef_received; }
bool nodedefReceived() bool nodedefReceived()
@ -319,6 +345,8 @@ private:
// Insert a media file appropriately into the appropriate manager // Insert a media file appropriately into the appropriate manager
bool loadMedia(const std::string &data, const std::string &filename); bool loadMedia(const std::string &data, const std::string &filename);
void request_media(const core::list<MediaRequest> &file_requests);
// Virtual methods from con::PeerHandler // Virtual methods from con::PeerHandler
void peerAdded(con::Peer *peer); void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout); void deletingPeer(con::Peer *peer, bool timeout);
@ -347,6 +375,7 @@ private:
MtEventManager *m_event; MtEventManager *m_event;
MeshUpdateThread m_mesh_update_thread; MeshUpdateThread m_mesh_update_thread;
core::list<MediaFetchThread*> m_media_fetch_threads;
ClientEnvironment m_env; ClientEnvironment m_env;
con::Connection m_con; con::Connection m_con;
IrrlichtDevice *m_device; IrrlichtDevice *m_device;
@ -375,8 +404,9 @@ private:
FileCache m_media_cache; FileCache m_media_cache;
// Mapping from media file name to SHA1 checksum // Mapping from media file name to SHA1 checksum
core::map<std::string, std::string> m_media_name_sha1_map; core::map<std::string, std::string> m_media_name_sha1_map;
float m_media_receive_progress; bool m_media_receive_started;
bool m_media_received; u32 m_media_count;
u32 m_media_received_count;
bool m_itemdef_received; bool m_itemdef_received;
bool m_nodedef_received; bool m_nodedef_received;
friend class FarMesh; friend class FarMesh;

View File

@ -77,9 +77,11 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
GENERIC_CMD_SET_ATTACHMENT GENERIC_CMD_SET_ATTACHMENT
PROTOCOL_VERSION 15: PROTOCOL_VERSION 15:
Serialization format changes Serialization format changes
PROTOCOL_VERSION 16:
TOCLIENT_SHOW_FORMSPEC
*/ */
#define LATEST_PROTOCOL_VERSION 15 #define LATEST_PROTOCOL_VERSION 16
// Server's supported network protocol range // Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13 #define SERVER_PROTOCOL_VERSION_MIN 13
@ -267,6 +269,8 @@ enum ToClientCommand
u32 length of data u32 length of data
data data
} }
u16 length of remote media server url (if applicable)
string url
*/ */
TOCLIENT_TOOLDEF = 0x39, TOCLIENT_TOOLDEF = 0x39,
@ -352,6 +356,12 @@ enum ToClientCommand
u8[len] name u8[len] name
[2] serialized inventory [2] serialized inventory
*/ */
TOCLIENT_SHOW_FORMSPEC = 0x44,
/*
[0] u16 command
u16 len
u8[len] formspec
*/
}; };
enum ToServerCommand enum ToServerCommand
@ -571,6 +581,10 @@ enum ToServerCommand
} }
*/ */
TOSERVER_RECEIVED_MEDIA = 0x41,
/*
u16 command
*/
}; };
#endif #endif

View File

@ -7,6 +7,7 @@
#define CMAKE_VERSION_STRING "@VERSION_STRING@" #define CMAKE_VERSION_STRING "@VERSION_STRING@"
#define CMAKE_RUN_IN_PLACE @RUN_IN_PLACE@ #define CMAKE_RUN_IN_PLACE @RUN_IN_PLACE@
#define CMAKE_USE_GETTEXT @USE_GETTEXT@ #define CMAKE_USE_GETTEXT @USE_GETTEXT@
#define CMAKE_USE_CURL @USE_CURL@
#define CMAKE_USE_SOUND @USE_SOUND@ #define CMAKE_USE_SOUND @USE_SOUND@
#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@" #define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"

View File

@ -11,6 +11,7 @@
#define RUN_IN_PLACE 0 #define RUN_IN_PLACE 0
#define USE_GETTEXT 0 #define USE_GETTEXT 0
#define USE_SOUND 0 #define USE_SOUND 0
#define USE_CURL 0
#define STATIC_SHAREDIR "" #define STATIC_SHAREDIR ""
#define BUILD_INFO "non-cmake" #define BUILD_INFO "non-cmake"
@ -26,6 +27,8 @@
#define USE_GETTEXT CMAKE_USE_GETTEXT #define USE_GETTEXT CMAKE_USE_GETTEXT
#undef USE_SOUND #undef USE_SOUND
#define USE_SOUND CMAKE_USE_SOUND #define USE_SOUND CMAKE_USE_SOUND
#undef USE_CURL
#define USE_CURL CMAKE_USE_CURL
#undef STATIC_SHAREDIR #undef STATIC_SHAREDIR
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR #define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
#undef BUILD_INFO #undef BUILD_INFO

View File

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h" #include "content_sao.h"
#include "settings.h" #include "settings.h"
#include "mapblock.h" // For getNodeBlockPos #include "mapblock.h" // For getNodeBlockPos
#include "mapgen.h" // For mapgen::make_tree #include "treegen.h" // For treegen::make_tree
#include "map.h" #include "map.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -37,7 +37,7 @@ public:
virtual std::set<std::string> getTriggerContents() virtual std::set<std::string> getTriggerContents()
{ {
std::set<std::string> s; std::set<std::string> s;
s.insert("dirt"); s.insert("mapgen_dirt");
return s; return s;
} }
virtual float getTriggerInterval() virtual float getTriggerInterval()
@ -54,7 +54,7 @@ public:
!ndef->get(n_top).isLiquid() && !ndef->get(n_top).isLiquid() &&
n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13) n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13)
{ {
n.setContent(ndef->getId("dirt_with_grass")); n.setContent(ndef->getId("mapgen_dirt_with_grass"));
map->addNodeWithEvent(p, n); map->addNodeWithEvent(p, n);
} }
} }
@ -67,7 +67,7 @@ public:
virtual std::set<std::string> getTriggerContents() virtual std::set<std::string> getTriggerContents()
{ {
std::set<std::string> s; std::set<std::string> s;
s.insert("dirt_with_grass"); s.insert("mapgen_dirt_with_grass");
return s; return s;
} }
virtual float getTriggerInterval() virtual float getTriggerInterval()
@ -83,7 +83,7 @@ public:
if(!ndef->get(n_top).light_propagates || if(!ndef->get(n_top).light_propagates ||
ndef->get(n_top).isLiquid()) ndef->get(n_top).isLiquid())
{ {
n.setContent(ndef->getId("dirt")); n.setContent(ndef->getId("mapgen_dirt"));
map->addNodeWithEvent(p, n); map->addNodeWithEvent(p, n);
} }
} }
@ -118,7 +118,7 @@ public:
v3s16 tree_blockp = getNodeBlockPos(tree_p); v3s16 tree_blockp = getNodeBlockPos(tree_p);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1)); vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
bool is_apple_tree = myrand()%4 == 0; bool is_apple_tree = myrand()%4 == 0;
mapgen::make_tree(vmanip, tree_p, is_apple_tree, ndef); treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef);
vmanip.blitBackAll(&modified_blocks); vmanip.blitBackAll(&modified_blocks);
// update lighting // update lighting

View File

@ -356,8 +356,11 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
m_last_sent_position_timer(0), m_last_sent_position_timer(0),
m_last_sent_move_precision(0), m_last_sent_move_precision(0),
m_armor_groups_sent(false), m_armor_groups_sent(false),
m_animation_speed(0),
m_animation_blend(0),
m_animation_sent(false), m_animation_sent(false),
m_bone_position_sent(false), m_bone_position_sent(false),
m_attachment_parent_id(0),
m_attachment_sent(false) m_attachment_sent(false)
{ {
// Only register type if no environment supplied // Only register type if no environment supplied

View File

@ -43,6 +43,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_rangeselect", "KEY_KEY_R"); settings->setDefault("keymap_rangeselect", "KEY_KEY_R");
settings->setDefault("keymap_freemove", "KEY_KEY_K"); settings->setDefault("keymap_freemove", "KEY_KEY_K");
settings->setDefault("keymap_fastmove", "KEY_KEY_J"); settings->setDefault("keymap_fastmove", "KEY_KEY_J");
settings->setDefault("keymap_noclip", "KEY_KEY_H");
settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_screenshot", "KEY_F12");
settings->setDefault("keymap_toggle_hud", "KEY_F1"); settings->setDefault("keymap_toggle_hud", "KEY_F1");
settings->setDefault("keymap_toggle_chat", "KEY_F2"); settings->setDefault("keymap_toggle_chat", "KEY_F2");
@ -55,6 +56,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("anaglyph", "false"); settings->setDefault("anaglyph", "false");
settings->setDefault("anaglyph_strength", "0.1"); settings->setDefault("anaglyph_strength", "0.1");
settings->setDefault("aux1_descends", "false"); settings->setDefault("aux1_descends", "false");
settings->setDefault("doubletap_jump", "false");
// Some (temporary) keys for debugging // Some (temporary) keys for debugging
settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P");
@ -95,6 +97,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("shader_path", ""); settings->setDefault("shader_path", "");
settings->setDefault("video_driver", "opengl"); settings->setDefault("video_driver", "opengl");
settings->setDefault("free_move", "false"); settings->setDefault("free_move", "false");
settings->setDefault("noclip", "false");
settings->setDefault("continuous_forward", "false"); settings->setDefault("continuous_forward", "false");
settings->setDefault("fast_move", "false"); settings->setDefault("fast_move", "false");
settings->setDefault("invert_mouse", "false"); settings->setDefault("invert_mouse", "false");
@ -106,6 +109,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("opaque_water", "false"); settings->setDefault("opaque_water", "false");
settings->setDefault("console_color", "(0,0,0)"); settings->setDefault("console_color", "(0,0,0)");
settings->setDefault("console_alpha", "200"); settings->setDefault("console_alpha", "200");
settings->setDefault("selectionbox_color", "(0,0,0)");
settings->setDefault("crosshair_color", "(255,255,255)");
settings->setDefault("crosshair_alpha", "255");
settings->setDefault("enable_sound", "true"); settings->setDefault("enable_sound", "true");
settings->setDefault("sound_volume", "0.8"); settings->setDefault("sound_volume", "0.8");
settings->setDefault("desynchronize_mapblock_texture_animation", "true"); settings->setDefault("desynchronize_mapblock_texture_animation", "true");
@ -116,6 +122,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("trilinear_filter", "false"); settings->setDefault("trilinear_filter", "false");
settings->setDefault("preload_item_visuals", "true"); settings->setDefault("preload_item_visuals", "true");
settings->setDefault("enable_shaders", "2"); settings->setDefault("enable_shaders", "2");
settings->setDefault("repeat_rightclick_time", "0.25");
settings->setDefault("media_fetch_threads", "8");
// Server stuff // Server stuff
// "map-dir" doesn't exist by default. // "map-dir" doesn't exist by default.
@ -156,5 +165,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("congestion_control_aim_rtt", "0.2"); settings->setDefault("congestion_control_aim_rtt", "0.2");
settings->setDefault("congestion_control_max_rate", "400"); settings->setDefault("congestion_control_max_rate", "400");
settings->setDefault("congestion_control_min_rate", "10"); settings->setDefault("congestion_control_min_rate", "10");
settings->setDefault("remote_media", "");
} }

View File

@ -819,6 +819,45 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
m_abms.push_back(ABMWithState(abm)); m_abms.push_back(ABMWithState(abm));
} }
bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
{
INodeDefManager *ndef = m_gamedef->ndef();
MapNode n_old = m_map->getNodeNoEx(p);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
scriptapi_node_on_destruct(m_lua, p, n_old);
// Replace node
bool succeeded = m_map->addNodeWithEvent(p, n);
if(!succeeded)
return false;
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
scriptapi_node_after_destruct(m_lua, p, n_old);
// Call constructor
if(ndef->get(n).has_on_construct)
scriptapi_node_on_construct(m_lua, p, n);
return true;
}
bool ServerEnvironment::removeNode(v3s16 p)
{
INodeDefManager *ndef = m_gamedef->ndef();
MapNode n_old = m_map->getNodeNoEx(p);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
scriptapi_node_on_destruct(m_lua, p, n_old);
// Replace with air
// This is slightly optimized compared to addNodeWithEvent(air)
bool succeeded = m_map->removeNodeWithEvent(p);
if(!succeeded)
return false;
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
scriptapi_node_after_destruct(m_lua, p, n_old);
// Air doesn't require constructor
return true;
}
std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius) std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
{ {
std::set<u16> objects; std::set<u16> objects;

View File

@ -284,6 +284,10 @@ public:
------------------------------------------- -------------------------------------------
*/ */
// Script-aware node setters
bool setNode(v3s16 p, const MapNode &n);
bool removeNode(v3s16 p);
// Find all active objects inside a radius around a point // Find all active objects inside a radius around a point
std::set<u16> getObjectsInsideRadius(v3f pos, float radius); std::set<u16> getObjectsInsideRadius(v3f pos, float radius);

View File

@ -192,6 +192,32 @@ public:
Client *m_client; Client *m_client;
}; };
class FormspecFormSource: public IFormSource
{
public:
FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
{
m_formspec = formspec;
m_game_formspec = game_formspec;
}
~FormspecFormSource()
{
*m_game_formspec = 0;
}
void setForm(std::string formspec) {
m_formspec = formspec;
}
std::string getForm()
{
return m_formspec;
}
std::string m_formspec;
FormspecFormSource** m_game_formspec;
};
/* /*
Hotbar draw routine Hotbar draw routine
*/ */
@ -901,6 +927,7 @@ void the_game(
bool simple_singleplayer_mode bool simple_singleplayer_mode
) )
{ {
FormspecFormSource* current_formspec = 0;
video::IVideoDriver* driver = device->getVideoDriver(); video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager(); scene::ISceneManager* smgr = device->getSceneManager();
@ -1280,9 +1307,12 @@ void the_game(
bool digging = false; bool digging = false;
bool ldown_for_dig = false; bool ldown_for_dig = false;
float damage_flash_timer = 0; float damage_flash = 0;
s16 farmesh_range = 20*MAP_BLOCKSIZE; s16 farmesh_range = 20*MAP_BLOCKSIZE;
float jump_timer = 0;
bool reset_jump_timer = false;
const float object_hit_delay = 0.2; const float object_hit_delay = 0.2;
float object_hit_delay_timer = 0.0; float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10; float time_from_last_punch = 10;
@ -1308,6 +1338,8 @@ void the_game(
float time_of_day = 0; float time_of_day = 0;
float time_of_day_smooth = 0; float time_of_day_smooth = 0;
float repeat_rightclick_timer = 0;
/* /*
Shader constants Shader constants
*/ */
@ -1329,6 +1361,10 @@ void the_game(
// NOTE: So we have to use getTime() and call run()s between them // NOTE: So we have to use getTime() and call run()s between them
u32 lasttime = device->getTimer()->getTime(); u32 lasttime = device->getTimer()->getTime();
LocalPlayer* player = client.getEnv().getLocalPlayer();
player->hurt_tilt_timer = 0;
player->hurt_tilt_strength = 0;
for(;;) for(;;)
{ {
if(device->run() == false || kill == true) if(device->run() == false || kill == true)
@ -1559,6 +1595,10 @@ void the_game(
// Input handler step() (used by the random input generator) // Input handler step() (used by the random input generator)
input->step(dtime); input->step(dtime);
// Increase timer for doubleclick of "jump"
if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2)
jump_timer += dtime;
/* /*
Launch menus and trigger stuff according to keys Launch menus and trigger stuff according to keys
*/ */
@ -1648,6 +1688,27 @@ void the_game(
statustext += L" (note: no 'fly' privilege)"; statustext += L" (note: no 'fly' privilege)";
} }
} }
else if(input->wasKeyDown(getKeySetting("keymap_jump")))
{
if(g_settings->getBool("doubletap_jump") && jump_timer < 0.2)
{
if(g_settings->getBool("free_move"))
{
g_settings->set("free_move","false");
statustext = L"free_move disabled";
statustext_time = 0;
}
else
{
g_settings->set("free_move","true");
statustext = L"free_move enabled";
statustext_time = 0;
if(!client.checkPrivilege("fly"))
statustext += L" (note: no 'fly' privilege)";
}
}
reset_jump_timer = true;
}
else if(input->wasKeyDown(getKeySetting("keymap_fastmove"))) else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
{ {
if(g_settings->getBool("fast_move")) if(g_settings->getBool("fast_move"))
@ -1665,6 +1726,23 @@ void the_game(
statustext += L" (note: no 'fast' privilege)"; statustext += L" (note: no 'fast' privilege)";
} }
} }
else if(input->wasKeyDown(getKeySetting("keymap_noclip")))
{
if(g_settings->getBool("noclip"))
{
g_settings->set("noclip","false");
statustext = L"noclip disabled";
statustext_time = 0;
}
else
{
g_settings->set("noclip","true");
statustext = L"noclip enabled";
statustext_time = 0;
if(!client.checkPrivilege("noclip"))
statustext += L" (note: no 'noclip' privilege)";
}
}
else if(input->wasKeyDown(getKeySetting("keymap_screenshot"))) else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
{ {
irr::video::IImage* const image = driver->createScreenShot(); irr::video::IImage* const image = driver->createScreenShot();
@ -1793,6 +1871,13 @@ void the_game(
statustext_time = 0; statustext_time = 0;
} }
// Reset jump_timer
if(!input->isKeyDown(getKeySetting("keymap_jump")) && reset_jump_timer)
{
reset_jump_timer = false;
jump_timer = 0.0;
}
// Handle QuicktuneShortcutter // Handle QuicktuneShortcutter
if(input->wasKeyDown(getKeySetting("keymap_quicktune_next"))) if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
quicktune.next(); quicktune.next();
@ -2011,10 +2096,13 @@ void the_game(
{ {
//u16 damage = event.player_damage.amount; //u16 damage = event.player_damage.amount;
//infostream<<"Player damage: "<<damage<<std::endl; //infostream<<"Player damage: "<<damage<<std::endl;
damage_flash_timer = 0.05;
if(event.player_damage.amount >= 2){ damage_flash += 100.0;
damage_flash_timer += 0.05 * event.player_damage.amount; damage_flash += 8.0 * event.player_damage.amount;
}
player->hurt_tilt_timer = 1.5;
player->hurt_tilt_strength = event.player_damage.amount/2;
player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 2.0, 10.0);
} }
else if(event.type == CE_PLAYER_FORCE_MOVE) else if(event.type == CE_PLAYER_FORCE_MOVE)
{ {
@ -2044,12 +2132,37 @@ void the_game(
/* Handle visualization */ /* Handle visualization */
damage_flash_timer = 0; damage_flash = 0;
LocalPlayer* player = client.getEnv().getLocalPlayer();
player->hurt_tilt_timer = 0;
player->hurt_tilt_strength = 0;
/*LocalPlayer* player = client.getLocalPlayer(); /*LocalPlayer* player = client.getLocalPlayer();
player->setPosition(player->getPosition() + v3f(0,-BS,0)); player->setPosition(player->getPosition() + v3f(0,-BS,0));
camera.update(player, busytime, screensize);*/ camera.update(player, busytime, screensize);*/
} }
else if (event.type == CE_SHOW_FORMSPEC)
{
if (current_formspec == 0)
{
/* Create menu */
current_formspec = new FormspecFormSource(*(event.show_formspec.formspec),&current_formspec);
GUIFormSpecMenu *menu =
new GUIFormSpecMenu(device, guiroot, -1,
&g_menumgr,
&client, gamedef);
menu->setFormSource(current_formspec);
menu->drop();
}
else
{
/* update menu */
current_formspec->setForm(*(event.show_formspec.formspec));
}
delete(event.show_formspec.formspec);
}
else if(event.type == CE_TEXTURES_UPDATED) else if(event.type == CE_TEXTURES_UPDATED)
{ {
update_wielded_item_trigger = true; update_wielded_item_trigger = true;
@ -2190,6 +2303,9 @@ void the_game(
bool left_punch = false; bool left_punch = false;
soundmaker.m_player_leftpunch_sound.name = ""; soundmaker.m_player_leftpunch_sound.name = "";
if(input->getRightState())
repeat_rightclick_timer += dtime;
if(playeritem_usable && input->getLeftState()) if(playeritem_usable && input->getLeftState())
{ {
if(input->getLeftClicked()) if(input->getLeftClicked())
@ -2330,8 +2446,10 @@ void the_game(
camera.setDigging(0); // left click animation camera.setDigging(0); // left click animation
} }
if(input->getRightClicked()) if(input->getRightClicked() ||
repeat_rightclick_timer >= g_settings->getFloat("repeat_rightclick_time"))
{ {
repeat_rightclick_timer = 0;
infostream<<"Ground right-clicked"<<std::endl; infostream<<"Ground right-clicked"<<std::endl;
// Sign special case, at least until formspec is properly implemented. // Sign special case, at least until formspec is properly implemented.
@ -2903,6 +3021,11 @@ void the_game(
if(show_hud) if(show_hud)
{ {
v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
u32 selectionbox_color_r = rangelim(myround(selectionbox_color.X), 0, 255);
u32 selectionbox_color_g = rangelim(myround(selectionbox_color.Y), 0, 255);
u32 selectionbox_color_b = rangelim(myround(selectionbox_color.Z), 0, 255);
for(std::vector<aabb3f>::const_iterator for(std::vector<aabb3f>::const_iterator
i = hilightboxes.begin(); i = hilightboxes.begin();
i != hilightboxes.end(); i++) i != hilightboxes.end(); i++)
@ -2912,7 +3035,7 @@ void the_game(
<<" max=" <<" max="
<<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")" <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
<<std::endl;*/ <<std::endl;*/
driver->draw3DBox(*i, video::SColor(255,0,0,0)); driver->draw3DBox(*i, video::SColor(255,selectionbox_color_r,selectionbox_color_g,selectionbox_color_b));
} }
} }
@ -2945,12 +3068,18 @@ void the_game(
*/ */
if(show_hud) if(show_hud)
{ {
v3f crosshair_color = g_settings->getV3F("crosshair_color");
u32 crosshair_color_r = rangelim(myround(crosshair_color.X), 0, 255);
u32 crosshair_color_g = rangelim(myround(crosshair_color.Y), 0, 255);
u32 crosshair_color_b = rangelim(myround(crosshair_color.Z), 0, 255);
u32 crosshair_alpha = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0), driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
displaycenter + core::vector2d<s32>(10,0), displaycenter + core::vector2d<s32>(10,0),
video::SColor(255,255,255,255)); video::SColor(crosshair_alpha,crosshair_color_r,crosshair_color_g,crosshair_color_b));
driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10), driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
displaycenter + core::vector2d<s32>(0,10), displaycenter + core::vector2d<s32>(0,10),
video::SColor(255,255,255,255)); video::SColor(crosshair_alpha,crosshair_color_r,crosshair_color_g,crosshair_color_b));
} }
} // timer } // timer
@ -2973,14 +3102,24 @@ void the_game(
/* /*
Damage flash Damage flash
*/ */
if(damage_flash_timer > 0.0) if(damage_flash > 0.0)
{ {
damage_flash_timer -= dtime; video::SColor color(std::min(damage_flash, 180.0f),180,0,0);
video::SColor color(128,255,0,0);
driver->draw2DRectangle(color, driver->draw2DRectangle(color,
core::rect<s32>(0,0,screensize.X,screensize.Y), core::rect<s32>(0,0,screensize.X,screensize.Y),
NULL); NULL);
damage_flash -= 100.0*dtime;
}
/*
Damage camera tilt
*/
if(player->hurt_tilt_timer > 0.0)
{
player->hurt_tilt_timer -= dtime*5;
if(player->hurt_tilt_timer < 0)
player->hurt_tilt_strength = 0;
} }
/* /*

View File

@ -293,7 +293,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
geom.X = stof(f.next(",")) * (float)imgsize.X; geom.X = stof(f.next(",")) * (float)imgsize.X;
geom.Y = stof(f.next(";")) * (float)imgsize.Y; geom.Y = stof(f.next(";")) * (float)imgsize.Y;
std::string name = f.next("]"); std::string name = f.next("]");
errorstream<<"item name="<<name infostream<<"item name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")" <<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl; <<std::endl;
@ -318,13 +318,16 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl; errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom)); m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
} }
else if(type == "field") else if(type == "field" || type == "textarea")
{ {
std::string fname = f.next(";"); std::string fname = f.next(";");
std::string flabel = f.next(";"); std::string flabel = f.next(";");
if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos) if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
{ {
if (type == "textarea")
errorstream<<"WARNING: Textarea connot be unpositioned"<<std::endl;
if(!bp_set) if(!bp_set)
{ {
rect = core::rect<s32>( rect = core::rect<s32>(
@ -339,7 +342,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
bp_set = 1; bp_set = 1;
} }
else if(bp_set == 2) else if(bp_set == 2)
errorstream<<"WARNING: invalid use of unpositioned field in inventory"<<std::endl; errorstream<<"WARNING: invalid use of unpositioned "<<type<<" in inventory"<<std::endl;
v2s32 pos = basepos; v2s32 pos = basepos;
pos.Y = ((m_fields.size()+2)*60); pos.Y = ((m_fields.size()+2)*60);
@ -353,14 +356,26 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y; pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
v2s32 geom; v2s32 geom;
geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X); geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
if (type == "textarea")
{
geom.Y = (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
pos.Y += 15;
}
else
{
pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2; pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
pos.Y -= 15;
geom.Y = 30;
}
rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
fname = f.next(";"); fname = f.next(";");
flabel = f.next(";"); flabel = f.next(";");
if(bp_set != 2) if(bp_set != 2)
errorstream<<"WARNING: invalid use of positioned field without a size[] element"<<std::endl; errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
} }
@ -381,40 +396,39 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
258+m_fields.size() 258+m_fields.size()
); );
// three cases: field and no label, label and no field, label and field // three cases: field name and no label, label and field, label name and no field
if (flabel == "") gui::IGUIEditBox *e;
if (fname == "")
{ {
spec.send = true; // spec field id to 0, this stops submit searching for a value that isn't there
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
Environment->setFocus(e);
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
}
else if (fname == "")
{
// set spec field id to 0, this stops submit searching for a value that isn't there
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
} }
else else
{ {
spec.send = true; spec.send = true;
gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
Environment->setFocus(e); Environment->setFocus(e);
rect.UpperLeftCorner.Y -= 15;
rect.LowerRightCorner.Y -= 15;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
if (type == "textarea")
{
e->setMultiLine(true);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
} else {
irr::SEvent evt; irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END; evt.KeyInput.Key = KEY_END;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.PressedDown = true; evt.KeyInput.PressedDown = true;
e->OnEvent(evt); e->OnEvent(evt);
} }
if (flabel != "")
{
rect.UpperLeftCorner.Y -= 15;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
}
}
m_fields.push_back(spec); m_fields.push_back(spec);
} }
else if(type == "label") else if(type == "label")
@ -808,6 +822,28 @@ void GUIFormSpecMenu::drawMenu()
NULL/*&AbsoluteClippingRect*/, colors, true); NULL/*&AbsoluteClippingRect*/, colors, true);
} }
/*
Draw item images
*/
for(u32 i=0; i<m_itemimages.size(); i++)
{
const ImageDrawSpec &spec = m_itemimages[i];
IItemDefManager *idef = m_gamedef->idef();
ItemStack item;
item.deSerialize(spec.name, idef);
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
// Image size on screen
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
driver->draw2DImage(texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
/* /*
Draw items Draw items
Phase 0: Item slot rectangles Phase 0: Item slot rectangles

View File

@ -46,6 +46,7 @@ enum
GUI_ID_KEY_FLY_BUTTON, GUI_ID_KEY_FLY_BUTTON,
GUI_ID_KEY_FAST_BUTTON, GUI_ID_KEY_FAST_BUTTON,
GUI_ID_KEY_JUMP_BUTTON, GUI_ID_KEY_JUMP_BUTTON,
GUI_ID_KEY_NOCLIP_BUTTON,
GUI_ID_KEY_CHAT_BUTTON, GUI_ID_KEY_CHAT_BUTTON,
GUI_ID_KEY_CMD_BUTTON, GUI_ID_KEY_CMD_BUTTON,
GUI_ID_KEY_CONSOLE_BUTTON, GUI_ID_KEY_CONSOLE_BUTTON,
@ -56,6 +57,7 @@ enum
GUI_ID_KEY_RANGE_BUTTON, GUI_ID_KEY_RANGE_BUTTON,
// other // other
GUI_ID_CB_AUX1_DESCENDS, GUI_ID_CB_AUX1_DESCENDS,
GUI_ID_CB_DOUBLETAP_JUMP,
}; };
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
@ -148,6 +150,20 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this, Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down")); GUI_ID_CB_AUX1_DESCENDS, wgettext("\"Use\" = climb down"));
} }
offset += v2s32(0, 25);
}
{
s32 option_x = offset.X + 10;
s32 option_y = offset.Y;
u32 option_w = 220;
{
core::rect<s32> rect(0, 0, option_w, 30);
rect += topleft + v2s32(option_x, option_y);
Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
GUI_ID_CB_DOUBLETAP_JUMP, wgettext("Doubltap \"jump\" to toogle fly"));
}
offset += v2s32(0, 25);
} }
{ {
@ -196,6 +212,11 @@ bool GUIKeyChangeMenu::acceptInput()
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked()); g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
} }
{
gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
}
clearKeyCache(); clearKeyCache();
return true; return true;
} }
@ -362,6 +383,7 @@ void GUIKeyChangeMenu::init_keys()
this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, "Console", "keymap_console"); 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_FLY_BUTTON, "Toggle fly", "keymap_freemove");
this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove"); this->add_key(GUI_ID_KEY_FAST_BUTTON, "Toggle fast", "keymap_fastmove");
this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, "Toggle noclip", "keymap_noclip");
this->add_key(GUI_ID_KEY_RANGE_BUTTON, "Range select", "keymap_rangeselect"); 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"); this->add_key(GUI_ID_KEY_DUMP_BUTTON, "Print stacks", "keymap_print_debug_stacks");
} }

View File

@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
using namespace irr; using namespace irr;
// Irrlicht 1.8+ defines 64bit unsigned symbol in irrTypes.h
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
#ifdef _MSC_VER #ifdef _MSC_VER
// Windows // Windows
typedef unsigned long long u64; typedef unsigned long long u64;
@ -33,6 +35,7 @@ using namespace irr;
typedef uint64_t u64; typedef uint64_t u64;
//typedef unsigned long long u64; //typedef unsigned long long u64;
#endif #endif
#endif
#endif #endif

View File

@ -260,7 +260,7 @@ KeyPress::KeyPress(const char *name)
try { try {
Key = keyname_to_keycode(name); Key = keyname_to_keycode(name);
m_name = name; m_name = name;
if (strlen(name) > 8) { if (strlen(name) > 8 && strncmp(name, "KEY_KEY_", 8) == 0) {
int chars_read = mbtowc(&Char, name + 8, 1); int chars_read = mbtowc(&Char, name + 8, 1);
assert (chars_read == 1 && "unexpected multibyte character"); assert (chars_read == 1 && "unexpected multibyte character");
} else } else

View File

@ -73,9 +73,11 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
return; return;
} }
// Skip collision detection if a special movement mode is used // Skip collision detection if noclip mode is used
bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
bool free_move = fly_allowed && g_settings->getBool("free_move"); bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
g_settings->getBool("noclip");
bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
if(free_move) if(free_move)
{ {
position += m_speed * dtime; position += m_speed * dtime;
@ -157,7 +159,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
If sneaking, keep in range from the last walked node and don't If sneaking, keep in range from the last walked node and don't
fall off from it fall off from it
*/ */
if(control.sneak && m_sneak_node_exists) if(control.sneak && m_sneak_node_exists && !g_settings->getBool("free_move"))
{ {
f32 maxd = 0.5*BS + sneak_max; f32 maxd = 0.5*BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS); v3f lwn_f = intToFloat(m_sneak_node, BS);
@ -300,7 +302,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
Report collisions Report collisions
*/ */
bool bouncy_jump = false; bool bouncy_jump = false;
if(collision_info) // Dont report if flying
if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
{ {
for(size_t i=0; i<result.collisions.size(); i++){ for(size_t i=0; i<result.collisions.size(); i++){
const CollisionInfo &info = result.collisions[i]; const CollisionInfo &info = result.collisions[i];
@ -396,13 +399,13 @@ void LocalPlayer::applyControl(float dtime)
// Whether superspeed mode is used or not // Whether superspeed mode is used or not
bool superspeed = false; bool superspeed = false;
// Old descend control
if(g_settings->getBool("aux1_descends"))
{
// If free movement and fast movement, always move fast // If free movement and fast movement, always move fast
if(free_move && fast_move) if(free_move && fast_move)
superspeed = true; superspeed = true;
// Old descend control
if(g_settings->getBool("aux1_descends"))
{
// Auxiliary button 1 (E) // Auxiliary button 1 (E)
if(control.aux1) if(control.aux1)
{ {
@ -437,10 +440,9 @@ void LocalPlayer::applyControl(float dtime)
// Auxiliary button 1 (E) // Auxiliary button 1 (E)
if(control.aux1) if(control.aux1)
{ {
if(!free_move && !is_climbing) if(!is_climbing)
{ {
// If not free movement but fast is allowed, aux1 is // aux1 is "Turbo button"
// "Turbo button"
if(fast_move) if(fast_move)
superspeed = true; superspeed = true;
} }
@ -452,7 +454,7 @@ void LocalPlayer::applyControl(float dtime)
{ {
// In free movement mode, sneak descends // In free movement mode, sneak descends
v3f speed = getSpeed(); v3f speed = getSpeed();
if(fast_move) if(fast_move && control.aux1)
speed.Y = -20*BS; speed.Y = -20*BS;
else else
speed.Y = -walkspeed_max; speed.Y = -walkspeed_max;
@ -494,10 +496,20 @@ void LocalPlayer::applyControl(float dtime)
if(free_move) if(free_move)
{ {
v3f speed = getSpeed(); v3f speed = getSpeed();
if(g_settings->getBool("aux1_descends"))
{
if(fast_move) if(fast_move)
speed.Y = 20*BS; speed.Y = 20*BS;
else else
speed.Y = walkspeed_max; speed.Y = walkspeed_max;
} else {
if(fast_move && control.aux1)
speed.Y = 20*BS;
else
speed.Y = walkspeed_max;
}
setSpeed(speed); setSpeed(speed);
} }
else if(m_can_jump) else if(m_can_jump)
@ -537,7 +549,7 @@ void LocalPlayer::applyControl(float dtime)
// The speed of the player (Y is ignored) // The speed of the player (Y is ignored)
if(superspeed) if(superspeed)
speed = speed.normalize() * walkspeed_max * 5.0; speed = speed.normalize() * walkspeed_max * 5.0;
else if(control.sneak) else if(control.sneak && !free_move)
speed = speed.normalize() * walkspeed_max / 3.0; speed = speed.normalize() * walkspeed_max / 3.0;
else else
speed = speed.normalize() * walkspeed_max; speed = speed.normalize() * walkspeed_max;
@ -545,7 +557,7 @@ void LocalPlayer::applyControl(float dtime)
f32 inc = walk_acceleration * BS * dtime; f32 inc = walk_acceleration * BS * dtime;
// Faster acceleration if fast and free movement // Faster acceleration if fast and free movement
if(free_move && fast_move) if(free_move && fast_move && superspeed)
inc = walk_acceleration * BS * dtime * 10; inc = walk_acceleration * BS * dtime * 10;
// Accelerate to target speed with maximum increment // Accelerate to target speed with maximum increment

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxelalgorithms.h" #include "voxelalgorithms.h"
#include "profiler.h" #include "profiler.h"
#include "main.h" // For g_profiler #include "main.h" // For g_profiler
#include "treegen.h"
namespace mapgen namespace mapgen
{ {
@ -120,174 +121,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
} }
#endif #endif
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_tree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
MapNode applenode(ndef->getId("mapgen_apple"));
s16 trunk_h = myrand_range(4, 5);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<7; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1) {
bool is_apple = myrand_range(0,99) < 10;
if(is_apple_tree && is_apple) {
vmanip.m_data[vi] = applenode;
} else {
vmanip.m_data[vi] = leavesnode;
}
}
}
}
#if 0 #if 0
static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_jungletree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
for(s16 x=-1; x<=1; x++)
for(s16 z=-1; z<=1; z++)
{
if(myrand_range(0, 2) == 0)
continue;
v3s16 p1 = p0 + v3s16(x,0,z);
v3s16 p2 = p0 + v3s16(x,-1,z);
if(vmanip.m_area.contains(p2)
&& vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
else if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
}
s16 trunk_h = myrand_range(8, 12);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<30; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1)
vmanip.m_data[vi] = leavesnode;
}
}
static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0, static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef) INodeDefManager *ndef)
@ -2239,7 +2073,7 @@ void make_block(BlockMakeData *data)
} }
p.Y++; p.Y++;
// Make a tree // Make a tree
make_tree(vmanip, p, false, ndef); treegen::make_tree(vmanip, p, false, ndef);
} }
} }
} }

View File

@ -42,10 +42,6 @@ namespace mapgen
// Main map generation routine // Main map generation routine
void make_block(BlockMakeData *data); void make_block(BlockMakeData *data);
// Add a tree
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef);
/* /*
These are used by FarMesh These are used by FarMesh
*/ */

View File

@ -209,6 +209,9 @@ public:
u16 hp; u16 hp;
float hurt_tilt_timer;
float hurt_tilt_strength;
u16 peer_id; u16 peer_id;
std::string inventory_formspec; std::string inventory_formspec;

View File

@ -48,6 +48,7 @@ extern "C" {
#include "noise.h" // PseudoRandom for LuaPseudoRandom #include "noise.h" // PseudoRandom for LuaPseudoRandom
#include "util/pointedthing.h" #include "util/pointedthing.h"
#include "rollback.h" #include "rollback.h"
#include "treegen.h"
static void stackDump(lua_State *L, std::ostream &o) static void stackDump(lua_State *L, std::ostream &o)
{ {
@ -2048,6 +2049,43 @@ private:
return 1; return 1;
} }
// get_location() -> location (like minetest.get_inventory(location))
static int l_get_location(lua_State *L)
{
InvRef *ref = checkobject(L, 1);
const InventoryLocation &loc = ref->m_loc;
switch(loc.type){
case InventoryLocation::PLAYER:
lua_newtable(L);
lua_pushstring(L, "player");
lua_setfield(L, -2, "type");
lua_pushstring(L, loc.name.c_str());
lua_setfield(L, -2, "name");
return 1;
case InventoryLocation::NODEMETA:
lua_newtable(L);
lua_pushstring(L, "nodemeta");
lua_setfield(L, -2, "type");
push_v3s16(L, loc.p);
lua_setfield(L, -2, "name");
return 1;
case InventoryLocation::DETACHED:
lua_newtable(L);
lua_pushstring(L, "detached");
lua_setfield(L, -2, "type");
lua_pushstring(L, loc.name.c_str());
lua_setfield(L, -2, "name");
return 1;
case InventoryLocation::UNDEFINED:
case InventoryLocation::CURRENT_PLAYER:
break;
}
lua_newtable(L);
lua_pushstring(L, "undefined");
lua_setfield(L, -2, "type");
return 1;
}
public: public:
InvRef(const InventoryLocation &loc): InvRef(const InventoryLocation &loc):
m_loc(loc) m_loc(loc)
@ -2123,6 +2161,7 @@ const luaL_reg InvRef::methods[] = {
method(InvRef, room_for_item), method(InvRef, room_for_item),
method(InvRef, contains_item), method(InvRef, contains_item),
method(InvRef, remove_item), method(InvRef, remove_item),
method(InvRef, get_location),
{0,0} {0,0}
}; };
@ -3501,20 +3540,7 @@ private:
v3s16 pos = read_v3s16(L, 2); v3s16 pos = read_v3s16(L, 2);
MapNode n = readnode(L, 3, ndef); MapNode n = readnode(L, 3, ndef);
// Do it // Do it
MapNode n_old = env->getMap().getNodeNoEx(pos); bool succeeded = env->setNode(pos, n);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
scriptapi_node_on_destruct(L, pos, n_old);
// Replace node
bool succeeded = env->getMap().addNodeWithEvent(pos, n);
if(succeeded){
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
scriptapi_node_after_destruct(L, pos, n_old);
// Call constructor
if(ndef->get(n).has_on_construct)
scriptapi_node_on_construct(L, pos, n);
}
lua_pushboolean(L, succeeded); lua_pushboolean(L, succeeded);
return 1; return 1;
} }
@ -3535,20 +3561,8 @@ private:
// parameters // parameters
v3s16 pos = read_v3s16(L, 2); v3s16 pos = read_v3s16(L, 2);
// Do it // Do it
MapNode n_old = env->getMap().getNodeNoEx(pos); bool succeeded = env->removeNode(pos);
// Call destructor
if(ndef->get(n_old).has_on_destruct)
scriptapi_node_on_destruct(L, pos, n_old);
// Replace with air
// This is slightly optimized compared to addNodeWithEvent(air)
bool succeeded = env->getMap().removeNodeWithEvent(pos);
if(succeeded){
// Call post-destructor
if(ndef->get(n_old).has_after_destruct)
scriptapi_node_after_destruct(L, pos, n_old);
}
lua_pushboolean(L, succeeded); lua_pushboolean(L, succeeded);
// Air doesn't require constructor
return 1; return 1;
} }
@ -4004,6 +4018,45 @@ private:
return 0; return 0;
} }
static int l_spawn_tree(lua_State *L)
{
EnvRef *o = checkobject(L, 1);
ServerEnvironment *env = o->m_env;
if(env == NULL) return 0;
v3s16 p0 = read_v3s16(L, 2);
treegen::TreeDef tree_def;
std::string trunk,leaves,fruit;
INodeDefManager *ndef = env->getGameDef()->ndef();
if(lua_istable(L, 3))
{
getstringfield(L, 3, "axiom", tree_def.initial_axiom);
getstringfield(L, 3, "rules_a", tree_def.rules_a);
getstringfield(L, 3, "rules_b", tree_def.rules_b);
getstringfield(L, 3, "rules_c", tree_def.rules_a);
getstringfield(L, 3, "rules_d", tree_def.rules_b);
getstringfield(L, 3, "trunk", trunk);
tree_def.trunknode=ndef->getId(trunk);
getstringfield(L, 3, "leaves", leaves);
tree_def.leavesnode=ndef->getId(leaves);
getintfield(L, 3, "angle", tree_def.angle);
getintfield(L, 3, "iterations", tree_def.iterations);
getintfield(L, 3, "random_level", tree_def.iterations_random_level);
getboolfield(L, 3, "thin_trunks", tree_def.thin_trunks);
getboolfield(L, 3, "fruit_tree", tree_def.fruit_tree);
if (tree_def.fruit_tree)
{
getstringfield(L, 3, "fruit", fruit);
tree_def.fruitnode=ndef->getId(fruit);
}
}
else
return 0;
treegen::spawn_ltree (env, p0, ndef, tree_def);
return 1;
}
public: public:
EnvRef(ServerEnvironment *env): EnvRef(ServerEnvironment *env):
m_env(env) m_env(env)
@ -4086,6 +4139,7 @@ const luaL_reg EnvRef::methods[] = {
method(EnvRef, find_nodes_in_area), method(EnvRef, find_nodes_in_area),
method(EnvRef, get_perlin), method(EnvRef, get_perlin),
method(EnvRef, clear_objects), method(EnvRef, clear_objects),
method(EnvRef, spawn_tree),
{0,0} {0,0}
}; };
@ -4858,6 +4912,21 @@ static int l_create_detached_inventory_raw(lua_State *L)
return 1; return 1;
} }
// create_detached_formspec_raw(name)
static int l_show_formspec(lua_State *L)
{
const char *playername = luaL_checkstring(L, 1);
const char *formspec = luaL_checkstring(L, 2);
if(get_server(L)->showFormspec(playername,formspec))
{
lua_pushboolean(L, true);
}else{
lua_pushboolean(L, false);
}
return 1;
}
// get_dig_params(groups, tool_capabilities[, time_from_last_punch]) // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
static int l_get_dig_params(lua_State *L) static int l_get_dig_params(lua_State *L)
{ {
@ -5187,6 +5256,7 @@ static const struct luaL_Reg minetest_f [] = {
{"unban_player_or_ip", l_unban_player_of_ip}, {"unban_player_or_ip", l_unban_player_of_ip},
{"get_inventory", l_get_inventory}, {"get_inventory", l_get_inventory},
{"create_detached_inventory_raw", l_create_detached_inventory_raw}, {"create_detached_inventory_raw", l_create_detached_inventory_raw},
{"show_formspec", l_show_formspec},
{"get_dig_params", l_get_dig_params}, {"get_dig_params", l_get_dig_params},
{"get_hit_params", l_get_hit_params}, {"get_hit_params", l_get_hit_params},
{"get_current_modname", l_get_current_modname}, {"get_current_modname", l_get_current_modname},

View File

@ -2885,6 +2885,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// (definitions and files) // (definitions and files)
getClient(peer_id)->definitions_sent = true; getClient(peer_id)->definitions_sent = true;
} }
else if(command == TOSERVER_RECEIVED_MEDIA) {
getClient(peer_id)->definitions_sent = true;
}
else if(command == TOSERVER_INTERACT) else if(command == TOSERVER_INTERACT)
{ {
std::string datastring((char*)&data[2], datasize-2); std::string datastring((char*)&data[2], datasize-2);
@ -3635,6 +3638,24 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
// Send as reliable // Send as reliable
m_con.Send(peer_id, 0, data, true); m_con.Send(peer_id, 0, data, true);
} }
void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
u8 buf[12];
// Write command
writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
os.write((char*)buf, 2);
os<<serializeLongString(formspec);
// Make data buffer
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
void Server::BroadcastChatMessage(const std::wstring &message) void Server::BroadcastChatMessage(const std::wstring &message)
{ {
@ -4217,6 +4238,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
os<<serializeString(j->name); os<<serializeString(j->name);
os<<serializeString(j->sha1_digest); os<<serializeString(j->sha1_digest);
} }
os<<serializeString(g_settings->get("remote_media"));
// Make data buffer // Make data buffer
std::string s = os.str(); std::string s = os.str();
@ -4224,7 +4246,6 @@ void Server::sendMediaAnnouncement(u16 peer_id)
// Send as reliable // Send as reliable
m_con.Send(peer_id, 0, data, true); m_con.Send(peer_id, 0, data, true);
} }
struct SendableMedia struct SendableMedia
@ -4575,6 +4596,20 @@ void Server::notifyPlayer(const char *name, const std::wstring msg)
SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg); SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
} }
bool Server::showFormspec(const char *playername, const std::string &formspec)
{
Player *player = m_env->getPlayer(playername);
if(!player)
{
infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
return false;
}
SendShowFormspecMessage(player->peer_id,formspec);
return true;
}
void Server::notifyPlayers(const std::wstring msg) void Server::notifyPlayers(const std::wstring msg)
{ {
BroadcastChatMessage(msg); BroadcastChatMessage(msg);
@ -4656,7 +4691,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
log->push_back(os.str()); log->push_back(os.str());
}else{ }else{
std::ostringstream os; std::ostringstream os;
os<<"Succesfully reverted step ("<<num_tried<<") "<<action.toString(); os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl; infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
if(log) if(log)
log->push_back(os.str()); log->push_back(os.str());

View File

@ -583,6 +583,7 @@ public:
m_async_fatal_error.set(error); m_async_fatal_error.set(error);
} }
bool showFormspec(const char *name, const std::string &formspec);
private: private:
// con::PeerHandler implementation. // con::PeerHandler implementation.
@ -620,6 +621,7 @@ private:
void SendMovePlayer(u16 peer_id); void SendMovePlayer(u16 peer_id);
void SendPlayerPrivileges(u16 peer_id); void SendPlayerPrivileges(u16 peer_id);
void SendPlayerInventoryFormspec(u16 peer_id); void SendPlayerInventoryFormspec(u16 peer_id);
void SendShowFormspecMessage(u16 peer_id, const std::string formspec);
/* /*
Send a node removal/addition event to all clients except ignore_id. Send a node removal/addition event to all clients except ignore_id.
Additionally, if far_players!=NULL, players further away than Additionally, if far_players!=NULL, players further away than

520
src/treegen.cpp Normal file
View File

@ -0,0 +1,520 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
2012 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "irr_v3d.h"
#include <stack>
#include "util/numeric.h"
#include "util/mathconstants.h"
#include "noise.h"
#include "map.h"
#include "environment.h"
#include "nodedef.h"
#include "treegen.h"
namespace treegen
{
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_tree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
MapNode applenode(ndef->getId("mapgen_apple"));
s16 trunk_h = myrand_range(4, 5);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<7; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1) {
bool is_apple = myrand_range(0,99) < 10;
if(is_apple_tree && is_apple) {
vmanip.m_data[vi] = applenode;
} else {
vmanip.m_data[vi] = leavesnode;
}
}
}
}
// L-System tree LUA spawner
void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
{
ServerMap *map = &env->getServerMap();
core::map<v3s16, MapBlock*> modified_blocks;
ManualMapVoxelManipulator vmanip(map);
v3s16 tree_blockp = getNodeBlockPos(p0);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
make_ltree (vmanip, p0, ndef, tree_definition);
vmanip.blitBackAll(&modified_blocks);
// update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
map->updateLighting(lighting_modified_blocks, modified_blocks);
// Send a MEET_OTHER event
MapEditEvent event;
event.type = MEET_OTHER;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
event.modified_blocks.insert(p, true);
}
map->dispatchEvent(&event);
}
//L-System tree generator
void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
TreeDef tree_definition)
{
MapNode dirtnode(ndef->getId("mapgen_dirt"));
// chance of inserting abcd rules
double prop_a = 9;
double prop_b = 8;
double prop_c = 7;
double prop_d = 6;
//randomize tree growth level, minimum=2
s16 iterations = tree_definition.iterations;
if (tree_definition.iterations_random_level>0)
iterations -= myrand_range(0,tree_definition.iterations_random_level);
if (iterations<2)
iterations=2;
s16 MAX_ANGLE_OFFSET = 5;
double angle_in_radians = (double)tree_definition.angle*M_PI/180;
double angleOffset_in_radians = (s16)(myrand_range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;
//initialize rotation matrix, position and stacks for branches
core::matrix4 rotation;
rotation=setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
v3f position = v3f(0,0,0);
std::stack <core::matrix4> stack_orientation;
std::stack <v3f> stack_position;
//generate axiom
std::string axiom = tree_definition.initial_axiom;
for(s16 i=0; i<iterations; i++)
{
std::string temp = "";
for(s16 j=0; j<(s16)axiom.size(); j++)
{
char axiom_char = axiom.at(j);
switch (axiom_char)
{
case 'A':
temp+=tree_definition.rules_a;
break;
case 'B':
temp+=tree_definition.rules_b;
break;
case 'C':
temp+=tree_definition.rules_c;
break;
case 'D':
temp+=tree_definition.rules_d;
break;
case 'a':
if (prop_a >= myrand_range(1,10))
temp+=tree_definition.rules_a;
break;
case 'b':
if (prop_b >= myrand_range(1,10))
temp+=tree_definition.rules_b;
break;
case 'c':
if (prop_c >= myrand_range(1,10))
temp+=tree_definition.rules_c;
break;
case 'd':
if (prop_d >= myrand_range(1,10))
temp+=tree_definition.rules_d;
break;
default:
temp+=axiom_char;
break;
}
}
axiom=temp;
}
//make sure tree is not floating in the air
if (tree_definition.thin_trunks == false)
{
make_tree_node_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
make_tree_node_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y-1,p0.Z+position.Z),dirtnode);
make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z+1),dirtnode);
make_tree_node_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y-1,p0.Z+position.Z-1),dirtnode);
}
/* build tree out of generated axiom
Key for Special L-System Symbols used in Axioms
G - move forward one unit with the pin down
F - move forward one unit with the pin up
A - replace with rules set A
B - replace with rules set B
C - replace with rules set C
D - replace with rules set D
a - replace with rules set A, chance 90%
b - replace with rules set B, chance 80%
c - replace with rules set C, chance 70%
d - replace with rules set D, chance 60%
+ - yaw the turtle right by angle degrees
- - yaw the turtle left by angle degrees
& - pitch the turtle down by angle degrees
^ - pitch the turtle up by angle degrees
/ - roll the turtle to the right by angle degrees
* - roll the turtle to the left by angle degrees
[ - save in stack current state info
] - recover from stack state info
*/
s16 x,y,z;
for(s16 i=0; i<(s16)axiom.size(); i++)
{
char axiom_char = axiom.at(i);
core::matrix4 temp_rotation;
temp_rotation.makeIdentity();
v3f dir;
switch (axiom_char)
{
case 'G':
dir = v3f(-1,0,0);
dir = transposeMatrix(rotation,dir);
position+=dir;
break;
case 'F':
make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
if (tree_definition.thin_trunks == false)
{
make_tree_trunk_placement(vmanip,v3f(p0.X+position.X+1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
make_tree_trunk_placement(vmanip,v3f(p0.X+position.X-1,p0.Y+position.Y,p0.Z+position.Z),tree_definition);
make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z+1),tree_definition);
make_tree_trunk_placement(vmanip,v3f(p0.X+position.X,p0.Y+position.Y,p0.Z+position.Z-1),tree_definition);
}
if (stack_orientation.empty() == false)
{
s16 size = 1;
for(x=-size; x<size+1; x++)
for(y=-size; y<size+1; y++)
for(z=-size; z<size+1; z++)
if (abs(x) == size && abs(y) == size && abs(z) == size)
{
make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x+1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x-1,p0.Y+position.Y+y,p0.Z+position.Z+z),tree_definition);
make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z+1),tree_definition);
make_tree_leaves_placement(vmanip,v3f(p0.X+position.X+x,p0.Y+position.Y+y,p0.Z+position.Z+z-1),tree_definition);
}
}
dir = v3f(1,0,0);
dir = transposeMatrix(rotation,dir);
position+=dir;
break;
// turtle commands
case '[':
stack_orientation.push(rotation);
stack_position.push(position);
break;
case ']':
rotation=stack_orientation.top();
stack_orientation.pop();
position=stack_position.top();
stack_position.pop();
break;
case '+':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
rotation*=temp_rotation;
break;
case '-':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
rotation*=temp_rotation;
break;
case '&':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
rotation*=temp_rotation;
break;
case '^':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
rotation*=temp_rotation;
break;
case '*':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
rotation*=temp_rotation;
break;
case '/':
temp_rotation.makeIdentity();
temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
rotation*=temp_rotation;
break;
default:
break;
}
}
}
void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
MapNode node)
{
v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
if(vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = node;
}
void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition)
{
v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
if(vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
}
void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition)
{
v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
if(vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
if (tree_definition.fruit_tree)
{
if (myrand_range(1,100) > 90+tree_definition.iterations)
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
else
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.leavesnode;
}
else if (myrand_range(1,100) > 20)
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.leavesnode;
}
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
{
double c = cos(angle);
double s = sin(angle);
double t = 1.0 - c;
double tx = t * axis.X;
double ty = t * axis.Y;
double tz = t * axis.Z;
double sx = s * axis.X;
double sy = s * axis.Y;
double sz = s * axis.Z;
M[0] = tx * axis.X + c;
M[1] = tx * axis.Y + sz;
M[2] = tx * axis.Z - sy;
M[4] = ty * axis.X - sz;
M[5] = ty * axis.Y + c;
M[6] = ty * axis.Z + sx;
M[8] = tz * axis.X + sy;
M[9] = tz * axis.Y - sx;
M[10] = tz * axis.Z + c;
return M;
}
v3f transposeMatrix(irr::core::matrix4 M, v3f v)
{
v3f translated;
double x = M[0] * v.X + M[4] * v.Y + M[8] * v.Z +M[12];
double y = M[1] * v.X + M[5] * v.Y + M[9] * v.Z +M[13];
double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
translated.X=x;
translated.Y=y;
translated.Z=z;
return translated;
}
#if 0
static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
INodeDefManager *ndef)
{
MapNode treenode(ndef->getId("mapgen_jungletree"));
MapNode leavesnode(ndef->getId("mapgen_leaves"));
for(s16 x=-1; x<=1; x++)
for(s16 z=-1; z<=1; z++)
{
if(myrand_range(0, 2) == 0)
continue;
v3s16 p1 = p0 + v3s16(x,0,z);
v3s16 p2 = p0 + v3s16(x,-1,z);
if(vmanip.m_area.contains(p2)
&& vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
else if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
}
s16 trunk_h = myrand_range(8, 12);
v3s16 p1 = p0;
for(s16 ii=0; ii<trunk_h; ii++)
{
if(vmanip.m_area.contains(p1))
vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
for(s32 i=0; i<leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
{
s16 d = 1;
for(s16 z=-d; z<=d; z++)
for(s16 y=-d; y<=d; y++)
for(s16 x=-d; x<=d; x++)
{
leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
}
// Add leaves randomly
for(u32 iii=0; iii<30; iii++)
{
s16 d = 1;
v3s16 p(
myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
);
for(s16 z=0; z<=d; z++)
for(s16 y=0; y<=d; y++)
for(s16 x=0; x<=d; x++)
{
leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
}
}
// Blit leaves to vmanip
for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
{
v3s16 p(x,y,z);
p += p1;
if(vmanip.m_area.contains(p) == false)
continue;
u32 vi = vmanip.m_area.index(p);
if(vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
continue;
u32 i = leaves_a.index(x,y,z);
if(leaves_d[i] == 1)
vmanip.m_data[vi] = leavesnode;
}
}
#endif
}; // namespace treegen

71
src/treegen.h Normal file
View File

@ -0,0 +1,71 @@
/*
Minetest-c55
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>,
2012 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef TREEGEN_HEADER
#define TREEGEN_HEADER
#include <matrix4.h>
class ManualMapVoxelManipulator;
class INodeDefManager;
namespace treegen
{
struct TreeDef
{
std::string initial_axiom;
std::string rules_a;
std::string rules_b;
std::string rules_c;
std::string rules_d;
MapNode trunknode;
MapNode leavesnode;
int angle;
int iterations;
int iterations_random_level;
bool thin_trunks;
bool fruit_tree;
MapNode fruitnode;
};
// Add default tree
void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef);
// Add L-Systems tree (used by engine)
void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
TreeDef tree_definition);
// Spawn L-systems tree from LUA
void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition);
// L-System tree gen helper functions
void make_tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
MapNode node);
void make_tree_trunk_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition);
void make_tree_leaves_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
TreeDef &tree_definition);
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle,v3f axis);
v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
}; // namespace treegen
#endif

View File

@ -41,3 +41,9 @@ std::string translatePassword(std::string playername, std::wstring password)
return pwd; return pwd;
} }
size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata) {
std::ostringstream *stream = (std::ostringstream*)userdata;
size_t count = size * nmemb;
stream->write(ptr, count);
return count;
}

View File

@ -282,6 +282,7 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen)
} }
std::string translatePassword(std::string playername, std::wstring password); std::string translatePassword(std::string playername, std::wstring password);
size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata);
#endif #endif

View File

@ -117,7 +117,7 @@ end
do do
local f_count = ProtoField.uint8("minetest.client.gotblocks_count", "Count", base.DEC) local f_count = ProtoField.uint8("minetest.client.gotblocks_count", "Count", base.DEC)
local f_block = ProtoField.bytes("minetset.client.gotblocks_block", "Block", base.DEC) local f_block = ProtoField.bytes("minetest.client.gotblocks_block", "Block", base.DEC)
local f_x = ProtoField.int16("minetest.client.gotblocks_x", "Block position X", base.DEC) local f_x = ProtoField.int16("minetest.client.gotblocks_x", "Block position X", base.DEC)
local f_y = ProtoField.int16("minetest.client.gotblocks_y", "Block position Y", base.DEC) local f_y = ProtoField.int16("minetest.client.gotblocks_y", "Block position Y", base.DEC)
local f_z = ProtoField.int16("minetest.client.gotblocks_z", "Block position Z", base.DEC) local f_z = ProtoField.int16("minetest.client.gotblocks_z", "Block position Z", base.DEC)
@ -151,7 +151,7 @@ end
do do
local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC) local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC)
local f_block = ProtoField.bytes("minetset.client.deletedblocks_block", "Block", base.DEC) local f_block = ProtoField.bytes("minetest.client.deletedblocks_block", "Block", base.DEC)
local f_x = ProtoField.int16("minetest.client.deletedblocks_x", "Block position X", base.DEC) local f_x = ProtoField.int16("minetest.client.deletedblocks_x", "Block position X", base.DEC)
local f_y = ProtoField.int16("minetest.client.deletedblocks_y", "Block position Y", base.DEC) local f_y = ProtoField.int16("minetest.client.deletedblocks_y", "Block position Y", base.DEC)
local f_z = ProtoField.int16("minetest.client.deletedblocks_z", "Block position Z", base.DEC) local f_z = ProtoField.int16("minetest.client.deletedblocks_z", "Block position Z", base.DEC)