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
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
set(VERSION_PATCH 4)
set(VERSION_PATCH 4-d1)
if(VERSION_EXTRA)
set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
endif()

View File

@ -192,11 +192,15 @@ function minetest.item_place_node(itemstack, placer, pointed_thing)
-- Add node and update
minetest.env:add_node(place_to, newnode)
local take_item = true
-- Run callback
if def.after_place_node then
-- Copy place_to because callback can modify it
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
-- 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 newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.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
itemstack:take_item()
if take_item then
itemstack:take_item()
end
return itemstack
end
@ -223,9 +231,19 @@ function minetest.item_place_object(itemstack, placer, pointed_thing)
end
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
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)
end
end
@ -367,6 +385,7 @@ minetest.nodedef_default = {
can_dig = nil,
on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
on_rightclick = nil,
on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
on_receive_fields = nil,
@ -456,7 +475,7 @@ minetest.noneitemdef_default = { -- This is used for the hand and unknown items
tool_capabilities = nil,
-- Interaction callbacks
on_place = nil,
on_place = redef_wrapper(minetest, 'item_place'),
on_drop = nil,
on_use = nil,
}

View File

@ -84,8 +84,9 @@ minetest.register_entity("__builtin:item", {
local p = self.object:getpos()
p.y = p.y - 0.3
local nn = minetest.env:get_node(p).name
-- If node is not registered or node is walkably solid
if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable then
-- If node is not registered or node is walkably solid and resting on nodebox
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
self.object:setvelocity({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",
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")

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
^ 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>]
^ x and y work as per field
^ 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
callbacks WILL LIKELY NOT BE RUN. Data should be saved at
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
^ If return true no item is taken from itemstack
^ Deprecated: Use on_construct or after_place_node in node definition instead
minetest.register_on_dignode(func(pos, oldnode, digger))
^ Called when a node has been dug.
@ -887,6 +891,9 @@ minetest.get_inventory(location) -> InvRef
minetest.create_detached_inventory(name, callbacks) -> InvRef
^ callbacks: See "Detached inventory callbacks"
^ 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:
minetest.inventorycube(img1, img2, img3)
@ -938,6 +945,8 @@ minetest.item_place_object(itemstack, placer, pointed_thing)
^ Place item as-is
minetest.item_place(itemstack, placer, pointed_thing)
^ 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)
^ Drop the item
minetest.item_eat(hp_change, replace_with_item)
@ -1062,6 +1071,60 @@ methods:
^ Return world-specific perlin noise (int(worldseed)+seeddiff)
- clear_objects()
^ 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:
- add_rat(pos): Add C++ rat object (no-op)
- add_firefly(pos): Add C++ firefly object (no-op)
@ -1173,6 +1236,8 @@ methods:
can be fully taken 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)
- get_location() -> location compatible to minetest.get_inventory(location)
-> {type="undefined"} in case location is not known
ItemStack: A stack of items.
- 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
^ default: nil
after_place_node = func(pos, placer),
after_place_node = func(pos, placer, itemstack),
^ Called after constructing node when node was placed using
minetest.item_place_node / minetest.env:place_node
^ If return true no item is taken from itemstack
^ default: nil
after_dig_node = func(pos, oldnode, oldmetadata, digger),
^ oldmetadata is in table format
@ -1421,6 +1487,8 @@ Node definition (register_node)
on_punch = func(pos, node, puncher),
^ default: minetest.node_punch
^ By default: does nothing
on_rightclick = func(pos, node, clicker),
^ default: nil
on_dig = func(pos, node, digger),
^ default: minetest.node_dig
^ By default: checks privileges, wears out tool and removes node

View File

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

View File

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

View File

@ -51,6 +51,8 @@
#keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false
# Doubletaping the jump key toogles fly mode
#doubletap_jump = false
# Some (temporary) keys for debugging
#keymap_print_debug_stacks = KEY_KEY_P
#keymap_quicktune_prev = KEY_HOME
@ -124,6 +126,12 @@
#console_color = (0,0,0)
# In-game chat console background alpha (opaqueness, between 0 and 255)
#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
#enable_sound = true
#sound_volume = 0.7
@ -140,6 +148,13 @@
# (1: low level shaders; not implemented)
# 2: enable high level shaders
#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
@ -217,3 +232,4 @@
#congestion_control_aim_rtt = 0.2
#congestion_control_max_rate = 400
#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(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
OPTION(ENABLE_GETTEXT "Use GetText for internationalization" 0)
@ -173,6 +188,7 @@ set(common_SRCS
log.cpp
content_sao.cpp
mapgen.cpp
treegen.cpp
content_nodemeta.cpp
content_mapnode.cpp
collision.cpp
@ -307,6 +323,16 @@ if(BUILD_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)
if(BUILD_SERVER)
@ -413,6 +439,9 @@ if(WIN32)
if(VORBISFILE_DLL)
install(FILES ${VORBISFILE_DLL} DESTINATION ${BINDIR})
endif()
if(CURL_DLL)
install(FILES ${CURL_DLL} DESTINATION ${BINDIR})
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->updateAbsolutePosition();
//Get camera tilt timer (hurt animation)
float cameratilt = fabs(fabs(player->hurt_tilt_timer-0.75)-0.75);
// Set head node transformation
m_headnode->setPosition(player->getEyeOffset());
m_headnode->setRotation(v3f(player->getPitch(), 0, 0));
m_headnode->setPosition(player->getEyeOffset()+v3f(0,cameratilt*-player->hurt_tilt_strength,0));
m_headnode->setRotation(v3f(player->getPitch(), 0, cameratilt*player->hurt_tilt_strength));
m_headnode->updateAbsolutePosition();
// 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 "IMeshCache.h"
#include "util/serialize.h"
#include "config.h"
#if USE_CURL
#include <curl/curl.h>
#endif
static std::string getMediaCacheDir()
{
return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
}
struct MediaRequest
{
std::string name;
MediaRequest(const std::string &name_=""):
name(name_)
{}
};
/*
QueuedMeshUpdate
*/
@ -223,6 +219,46 @@ void * MeshUpdateThread::Thread()
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(
IrrlichtDevice *device,
const char *playername,
@ -263,8 +299,9 @@ Client::Client(
m_password(password),
m_access_denied(false),
m_media_cache(getMediaCacheDir()),
m_media_receive_progress(0),
m_media_received(false),
m_media_receive_started(false),
m_media_count(0),
m_media_received_count(0),
m_itemdef_received(false),
m_nodedef_received(false),
m_time_of_day_set(false),
@ -296,6 +333,9 @@ Client::Client(
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()
@ -319,6 +359,10 @@ Client::~Client()
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)
@ -730,6 +774,62 @@ void Client::step(float dtime)
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
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;
}
/*
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()
{
DSTACK(__FUNCTION_NAME);
@ -1514,37 +1642,52 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
file_requests.push_back(MediaRequest(name));
}
std::string remote_media = "";
try {
remote_media = deSerializeString(is);
}
catch(SerializationError) {
// not supported by server or turned off
}
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();
i != file_requests.end(); i++) {
(*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
// notify server we received everything
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_RECEIVED_MEDIA);
std::string s = os.str();
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
// Send as reliable
Send(0, data, true);
}
ClientEvent event;
event.type = CE_TEXTURES_UPDATED;
m_client_event_queue.push_back(event);
/*
u16 command
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());
for(core::list<MediaRequest>::Iterator 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;
}
else if(command == TOCLIENT_MEDIA)
{
if (m_media_count == 0)
return;
std::string datastring((char*)&data[2], datasize-2);
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 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);
infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
<<num_bunches<<" files="<<num_files
<<" size="<<datasize<<std::endl;
for(int i=0; i<num_files; i++){
m_media_received_count++;
std::string name = deSerializeString(is);
std::string data = deSerializeLongString(is);
@ -1762,6 +1900,20 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
}
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
{
infostream<<"Client: Ignoring unknown command "
@ -2458,7 +2610,7 @@ void Client::afterContentReceived()
infostream<<"Client::afterContentReceived() started"<<std::endl;
assert(m_itemdef_received);
assert(m_nodedef_received);
assert(m_media_received);
assert(texturesReceived());
// remove the information about which checksum each texture
// 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 "filecache.h"
#include "localplayer.h"
#include "server.h"
#include "util/pointedthing.h"
struct MeshMakeData;
@ -129,13 +130,32 @@ public:
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
{
CE_NONE,
CE_PLAYER_DAMAGE,
CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN,
CE_TEXTURES_UPDATED
CE_TEXTURES_UPDATED,
CE_SHOW_FORMSPEC
};
struct ClientEvent
@ -157,6 +177,9 @@ struct ClientEvent
f32 camera_point_target_y;
f32 camera_point_target_z;
} deathscreen;
struct{
std::string* formspec;
} show_formspec;
struct{
} textures_updated;
};
@ -289,10 +312,13 @@ public:
{ return m_access_denied_reason; }
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()
{ return m_media_received; }
{ return m_media_receive_started && m_media_received_count == m_media_count; }
bool itemdefReceived()
{ return m_itemdef_received; }
bool nodedefReceived()
@ -318,7 +344,9 @@ private:
// Insert a media file appropriately into the appropriate manager
bool loadMedia(const std::string &data, const std::string &filename);
void request_media(const core::list<MediaRequest> &file_requests);
// Virtual methods from con::PeerHandler
void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout);
@ -347,6 +375,7 @@ private:
MtEventManager *m_event;
MeshUpdateThread m_mesh_update_thread;
core::list<MediaFetchThread*> m_media_fetch_threads;
ClientEnvironment m_env;
con::Connection m_con;
IrrlichtDevice *m_device;
@ -375,8 +404,9 @@ private:
FileCache m_media_cache;
// Mapping from media file name to SHA1 checksum
core::map<std::string, std::string> m_media_name_sha1_map;
float m_media_receive_progress;
bool m_media_received;
bool m_media_receive_started;
u32 m_media_count;
u32 m_media_received_count;
bool m_itemdef_received;
bool m_nodedef_received;
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
PROTOCOL_VERSION 15:
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
#define SERVER_PROTOCOL_VERSION_MIN 13
@ -267,6 +269,8 @@ enum ToClientCommand
u32 length of data
data
}
u16 length of remote media server url (if applicable)
string url
*/
TOCLIENT_TOOLDEF = 0x39,
@ -352,6 +356,12 @@ enum ToClientCommand
u8[len] name
[2] serialized inventory
*/
TOCLIENT_SHOW_FORMSPEC = 0x44,
/*
[0] u16 command
u16 len
u8[len] formspec
*/
};
enum ToServerCommand
@ -571,6 +581,10 @@ enum ToServerCommand
}
*/
TOSERVER_RECEIVED_MEDIA = 0x41,
/*
u16 command
*/
};
#endif

View File

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

View File

@ -11,6 +11,7 @@
#define RUN_IN_PLACE 0
#define USE_GETTEXT 0
#define USE_SOUND 0
#define USE_CURL 0
#define STATIC_SHAREDIR ""
#define BUILD_INFO "non-cmake"
@ -26,6 +27,8 @@
#define USE_GETTEXT CMAKE_USE_GETTEXT
#undef USE_SOUND
#define USE_SOUND CMAKE_USE_SOUND
#undef USE_CURL
#define USE_CURL CMAKE_USE_CURL
#undef STATIC_SHAREDIR
#define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
#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 "settings.h"
#include "mapblock.h" // For getNodeBlockPos
#include "mapgen.h" // For mapgen::make_tree
#include "treegen.h" // For treegen::make_tree
#include "map.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -37,7 +37,7 @@ public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("dirt");
s.insert("mapgen_dirt");
return s;
}
virtual float getTriggerInterval()
@ -54,7 +54,7 @@ public:
!ndef->get(n_top).isLiquid() &&
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);
}
}
@ -67,7 +67,7 @@ public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("dirt_with_grass");
s.insert("mapgen_dirt_with_grass");
return s;
}
virtual float getTriggerInterval()
@ -83,7 +83,7 @@ public:
if(!ndef->get(n_top).light_propagates ||
ndef->get(n_top).isLiquid())
{
n.setContent(ndef->getId("dirt"));
n.setContent(ndef->getId("mapgen_dirt"));
map->addNodeWithEvent(p, n);
}
}
@ -118,7 +118,7 @@ public:
v3s16 tree_blockp = getNodeBlockPos(tree_p);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
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);
// update lighting

View File

@ -356,8 +356,11 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
m_last_sent_position_timer(0),
m_last_sent_move_precision(0),
m_armor_groups_sent(false),
m_animation_speed(0),
m_animation_blend(0),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_parent_id(0),
m_attachment_sent(false)
{
// 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_freemove", "KEY_KEY_K");
settings->setDefault("keymap_fastmove", "KEY_KEY_J");
settings->setDefault("keymap_noclip", "KEY_KEY_H");
settings->setDefault("keymap_screenshot", "KEY_F12");
settings->setDefault("keymap_toggle_hud", "KEY_F1");
settings->setDefault("keymap_toggle_chat", "KEY_F2");
@ -55,6 +56,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("anaglyph", "false");
settings->setDefault("anaglyph_strength", "0.1");
settings->setDefault("aux1_descends", "false");
settings->setDefault("doubletap_jump", "false");
// Some (temporary) keys for debugging
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("video_driver", "opengl");
settings->setDefault("free_move", "false");
settings->setDefault("noclip", "false");
settings->setDefault("continuous_forward", "false");
settings->setDefault("fast_move", "false");
settings->setDefault("invert_mouse", "false");
@ -106,6 +109,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("opaque_water", "false");
settings->setDefault("console_color", "(0,0,0)");
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("sound_volume", "0.8");
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
@ -116,6 +122,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("trilinear_filter", "false");
settings->setDefault("preload_item_visuals", "true");
settings->setDefault("enable_shaders", "2");
settings->setDefault("repeat_rightclick_time", "0.25");
settings->setDefault("media_fetch_threads", "8");
// Server stuff
// "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_max_rate", "400");
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));
}
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> objects;

View File

@ -283,6 +283,10 @@ public:
Other stuff
-------------------------------------------
*/
// 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
std::set<u16> getObjectsInsideRadius(v3f pos, float radius);

View File

@ -192,6 +192,32 @@ public:
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
*/
@ -901,6 +927,7 @@ void the_game(
bool simple_singleplayer_mode
)
{
FormspecFormSource* current_formspec = 0;
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
@ -1280,9 +1307,12 @@ void the_game(
bool digging = false;
bool ldown_for_dig = false;
float damage_flash_timer = 0;
float damage_flash = 0;
s16 farmesh_range = 20*MAP_BLOCKSIZE;
float jump_timer = 0;
bool reset_jump_timer = false;
const float object_hit_delay = 0.2;
float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10;
@ -1308,6 +1338,8 @@ void the_game(
float time_of_day = 0;
float time_of_day_smooth = 0;
float repeat_rightclick_timer = 0;
/*
Shader constants
*/
@ -1329,6 +1361,10 @@ void the_game(
// NOTE: So we have to use getTime() and call run()s between them
u32 lasttime = device->getTimer()->getTime();
LocalPlayer* player = client.getEnv().getLocalPlayer();
player->hurt_tilt_timer = 0;
player->hurt_tilt_strength = 0;
for(;;)
{
if(device->run() == false || kill == true)
@ -1559,6 +1595,10 @@ void the_game(
// Input handler step() (used by the random input generator)
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
*/
@ -1648,6 +1688,27 @@ void the_game(
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")))
{
if(g_settings->getBool("fast_move"))
@ -1665,6 +1726,23 @@ void the_game(
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")))
{
irr::video::IImage* const image = driver->createScreenShot();
@ -1793,6 +1871,13 @@ void the_game(
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
if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
quicktune.next();
@ -2011,10 +2096,13 @@ void the_game(
{
//u16 damage = event.player_damage.amount;
//infostream<<"Player damage: "<<damage<<std::endl;
damage_flash_timer = 0.05;
if(event.player_damage.amount >= 2){
damage_flash_timer += 0.05 * event.player_damage.amount;
}
damage_flash += 100.0;
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)
{
@ -2044,12 +2132,37 @@ void the_game(
/* 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();
player->setPosition(player->getPosition() + v3f(0,-BS,0));
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)
{
update_wielded_item_trigger = true;
@ -2190,6 +2303,9 @@ void the_game(
bool left_punch = false;
soundmaker.m_player_leftpunch_sound.name = "";
if(input->getRightState())
repeat_rightclick_timer += dtime;
if(playeritem_usable && input->getLeftState())
{
if(input->getLeftClicked())
@ -2330,8 +2446,10 @@ void the_game(
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;
// Sign special case, at least until formspec is properly implemented.
@ -2903,6 +3021,11 @@ void the_game(
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
i = hilightboxes.begin();
i != hilightboxes.end(); i++)
@ -2912,7 +3035,7 @@ void the_game(
<<" max="
<<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
<<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)
{
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),
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),
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
@ -2973,14 +3102,24 @@ void the_game(
/*
Damage flash
*/
if(damage_flash_timer > 0.0)
if(damage_flash > 0.0)
{
damage_flash_timer -= dtime;
video::SColor color(128,255,0,0);
video::SColor color(std::min(damage_flash, 180.0f),180,0,0);
driver->draw2DRectangle(color,
core::rect<s32>(0,0,screensize.X,screensize.Y),
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.Y = stof(f.next(";")) * (float)imgsize.Y;
std::string name = f.next("]");
errorstream<<"item name="<<name
infostream<<"item name="<<name
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
@ -318,13 +318,16 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
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 flabel = f.next(";");
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)
{
rect = core::rect<s32>(
@ -339,7 +342,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
bp_set = 1;
}
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;
pos.Y = ((m_fields.size()+2)*60);
@ -353,20 +356,32 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
v2s32 geom;
geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
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 -= 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(";");
flabel = f.next(";");
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;
}
std::string odefault = f.next("]");
std::string fdefault;
// fdefault may contain a variable reference, which
// needs to be resolved from the node metadata
if(m_form_src)
@ -380,41 +395,40 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
narrow_to_wide(fdefault.c_str()),
258+m_fields.size()
);
// three cases: field and no label, label and no field, label and field
if (flabel == "")
{
spec.send = true;
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 == "")
// three cases: field name and no label, label and field, label name and no field
gui::IGUIEditBox *e;
if (fname == "")
{
// set spec field id to 0, this stops submit searching for a value that isn't there
// 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);
}
else
{
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);
rect.UpperLeftCorner.Y -= 15;
rect.LowerRightCorner.Y -= 15;
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
if (type == "textarea")
{
e->setMultiLine(true);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
} else {
irr::SEvent evt;
evt.KeyInput.Key = KEY_END;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.PressedDown = true;
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);
}
else if(type == "label")
@ -807,7 +821,29 @@ void GUIFormSpecMenu::drawMenu()
core::dimension2di(texture->getOriginalSize())),
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
Phase 0: Item slot rectangles

View File

@ -46,6 +46,7 @@ enum
GUI_ID_KEY_FLY_BUTTON,
GUI_ID_KEY_FAST_BUTTON,
GUI_ID_KEY_JUMP_BUTTON,
GUI_ID_KEY_NOCLIP_BUTTON,
GUI_ID_KEY_CHAT_BUTTON,
GUI_ID_KEY_CMD_BUTTON,
GUI_ID_KEY_CONSOLE_BUTTON,
@ -56,6 +57,7 @@ enum
GUI_ID_KEY_RANGE_BUTTON,
// other
GUI_ID_CB_AUX1_DESCENDS,
GUI_ID_CB_DOUBLETAP_JUMP,
};
GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
@ -148,6 +150,20 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
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)
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();
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_FLY_BUTTON, "Toggle fly", "keymap_freemove");
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_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;
// Irrlicht 1.8+ defines 64bit unsigned symbol in irrTypes.h
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
#ifdef _MSC_VER
// Windows
typedef unsigned long long u64;
@ -33,6 +35,7 @@ using namespace irr;
typedef uint64_t u64;
//typedef unsigned long long u64;
#endif
#endif
#endif

View File

@ -260,7 +260,7 @@ KeyPress::KeyPress(const char *name)
try {
Key = keyname_to_keycode(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);
assert (chars_read == 1 && "unexpected multibyte character");
} else

View File

@ -73,9 +73,11 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
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 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)
{
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
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;
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
*/
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++){
const CollisionInfo &info = result.collisions[i];
@ -396,13 +399,13 @@ void LocalPlayer::applyControl(float dtime)
// Whether superspeed mode is used or not
bool superspeed = false;
// If free movement and fast movement, always move fast
if(free_move && fast_move)
superspeed = true;
// Old descend control
if(g_settings->getBool("aux1_descends"))
{
// If free movement and fast movement, always move fast
if(free_move && fast_move)
superspeed = true;
// Auxiliary button 1 (E)
if(control.aux1)
{
@ -437,10 +440,9 @@ void LocalPlayer::applyControl(float dtime)
// Auxiliary button 1 (E)
if(control.aux1)
{
if(!free_move && !is_climbing)
if(!is_climbing)
{
// If not free movement but fast is allowed, aux1 is
// "Turbo button"
// aux1 is "Turbo button"
if(fast_move)
superspeed = true;
}
@ -452,7 +454,7 @@ void LocalPlayer::applyControl(float dtime)
{
// In free movement mode, sneak descends
v3f speed = getSpeed();
if(fast_move)
if(fast_move && control.aux1)
speed.Y = -20*BS;
else
speed.Y = -walkspeed_max;
@ -494,10 +496,20 @@ void LocalPlayer::applyControl(float dtime)
if(free_move)
{
v3f speed = getSpeed();
if(fast_move)
speed.Y = 20*BS;
else
speed.Y = walkspeed_max;
if(g_settings->getBool("aux1_descends"))
{
if(fast_move)
speed.Y = 20*BS;
else
speed.Y = walkspeed_max;
} else {
if(fast_move && control.aux1)
speed.Y = 20*BS;
else
speed.Y = walkspeed_max;
}
setSpeed(speed);
}
else if(m_can_jump)
@ -537,7 +549,7 @@ void LocalPlayer::applyControl(float dtime)
// The speed of the player (Y is ignored)
if(superspeed)
speed = speed.normalize() * walkspeed_max * 5.0;
else if(control.sneak)
else if(control.sneak && !free_move)
speed = speed.normalize() * walkspeed_max / 3.0;
else
speed = speed.normalize() * walkspeed_max;
@ -545,7 +557,7 @@ void LocalPlayer::applyControl(float dtime)
f32 inc = walk_acceleration * BS * dtime;
// Faster acceleration if fast and free movement
if(free_move && fast_move)
if(free_move && fast_move && superspeed)
inc = walk_acceleration * BS * dtime * 10;
// 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 "profiler.h"
#include "main.h" // For g_profiler
#include "treegen.h"
namespace mapgen
{
@ -120,174 +121,7 @@ static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
}
#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
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,
INodeDefManager *ndef)
@ -2239,7 +2073,7 @@ void make_block(BlockMakeData *data)
}
p.Y++;
// 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
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
*/

View File

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

View File

@ -48,6 +48,7 @@ extern "C" {
#include "noise.h" // PseudoRandom for LuaPseudoRandom
#include "util/pointedthing.h"
#include "rollback.h"
#include "treegen.h"
static void stackDump(lua_State *L, std::ostream &o)
{
@ -2048,6 +2049,43 @@ private:
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:
InvRef(const InventoryLocation &loc):
m_loc(loc)
@ -2123,6 +2161,7 @@ const luaL_reg InvRef::methods[] = {
method(InvRef, room_for_item),
method(InvRef, contains_item),
method(InvRef, remove_item),
method(InvRef, get_location),
{0,0}
};
@ -3501,20 +3540,7 @@ private:
v3s16 pos = read_v3s16(L, 2);
MapNode n = readnode(L, 3, ndef);
// Do it
MapNode n_old = env->getMap().getNodeNoEx(pos);
// 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);
}
bool succeeded = env->setNode(pos, n);
lua_pushboolean(L, succeeded);
return 1;
}
@ -3535,20 +3561,8 @@ private:
// parameters
v3s16 pos = read_v3s16(L, 2);
// Do it
MapNode n_old = env->getMap().getNodeNoEx(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);
}
bool succeeded = env->removeNode(pos);
lua_pushboolean(L, succeeded);
// Air doesn't require constructor
return 1;
}
@ -4004,6 +4018,45 @@ private:
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:
EnvRef(ServerEnvironment *env):
m_env(env)
@ -4086,6 +4139,7 @@ const luaL_reg EnvRef::methods[] = {
method(EnvRef, find_nodes_in_area),
method(EnvRef, get_perlin),
method(EnvRef, clear_objects),
method(EnvRef, spawn_tree),
{0,0}
};
@ -4858,6 +4912,21 @@ static int l_create_detached_inventory_raw(lua_State *L)
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])
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},
{"get_inventory", l_get_inventory},
{"create_detached_inventory_raw", l_create_detached_inventory_raw},
{"show_formspec", l_show_formspec},
{"get_dig_params", l_get_dig_params},
{"get_hit_params", l_get_hit_params},
{"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)
getClient(peer_id)->definitions_sent = true;
}
else if(command == TOSERVER_RECEIVED_MEDIA) {
getClient(peer_id)->definitions_sent = true;
}
else if(command == TOSERVER_INTERACT)
{
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
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)
{
@ -4217,6 +4238,7 @@ void Server::sendMediaAnnouncement(u16 peer_id)
os<<serializeString(j->name);
os<<serializeString(j->sha1_digest);
}
os<<serializeString(g_settings->get("remote_media"));
// Make data buffer
std::string s = os.str();
@ -4224,7 +4246,6 @@ void Server::sendMediaAnnouncement(u16 peer_id)
// Send as reliable
m_con.Send(peer_id, 0, data, true);
}
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);
}
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)
{
BroadcastChatMessage(msg);
@ -4656,7 +4691,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
log->push_back(os.str());
}else{
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;
if(log)
log->push_back(os.str());

View File

@ -583,6 +583,7 @@ public:
m_async_fatal_error.set(error);
}
bool showFormspec(const char *name, const std::string &formspec);
private:
// con::PeerHandler implementation.
@ -620,6 +621,7 @@ private:
void SendMovePlayer(u16 peer_id);
void SendPlayerPrivileges(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.
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;
}
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);
size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata);
#endif

View File

@ -117,7 +117,7 @@ end
do
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_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)
@ -151,7 +151,7 @@ end
do
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_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)