Merge pull request #13 from Bahamada/upstream_merge

Upstream merge
This commit is contained in:
Nils Dagsson Moskopp 2011-06-27 06:56:26 -07:00
commit 1c90f9fc2e
96 changed files with 143404 additions and 7498 deletions

2
.gitignore vendored

@ -1,4 +1,5 @@
map/* map/*
world/*
CMakeFiles/* CMakeFiles/*
src/CMakeFiles/* src/CMakeFiles/*
src/Makefile src/Makefile
@ -17,4 +18,5 @@ Makefile
cmake_install.cmake cmake_install.cmake
src/jthread/libjthread.a src/jthread/libjthread.a
debug.txt debug.txt
bin/debug.txt
minetestmapper/map.png minetestmapper/map.png

@ -1,4 +1,5 @@
map/* map/*
world/*
CMakeFiles/* CMakeFiles/*
src/CMakeFiles/* src/CMakeFiles/*
src/Makefile src/Makefile

@ -9,7 +9,7 @@ project(minetest)
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 2) set(VERSION_MINOR 2)
set(VERSION_PATCH 20110424_1_dev) set(VERSION_PATCH 20110618_0_dev)
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
# Configuration options # Configuration options

239
README.txt Normal file

@ -0,0 +1,239 @@
Minetest-c55
---------------
An InfiniMiner/Minecraft inspired game.
Copyright (c) 2010-2011 Perttu Ahola <celeron55@gmail.com>
(see source files for other contributors)
Further documentation:
----------------------
- Website: http://celeron.55.lt/~celeron55/minetest/
- Wiki: http://celeron.55.lt/~celeron55/minetest/wiki/
- Forum: http://celeron.55.lt/~celeron55/minetest/forum/
- doc/ directory of source distribution
This game is not finished:
--------------------------
- Don't expect it to work as well as a finished game will.
- Please report any bugs to me. debug.txt is useful.
Controls:
---------
- See the in-game pause menu
- Settable in the configuration file, see the section below.
Map directory:
--------------
- Map is stored in a directory, which can be removed to generate a new map.
- There is a command-line option for it: --map-dir
- For a RUN_IN_PLACE build, it is located in:
../map
- Otherwise something like this:
Windows: C:\Documents and Settings\user\Application Data\minetest\map
Linux: ~/.minetest/map
OS X: ~/Library/Application Support/minetest/map
Configuration file:
-------------------
- An optional configuration file can be used. See minetest.conf.example.
- Path to file can be passed as a parameter to the executable:
--config <path-to-file>
- Defaults:
- If built with -DRUN_IN_PLACE=1:
../minetest.conf
../../minetest.conf
- Otherwise something like this:
Windows: C:\Documents and Settings\user\Application Data\minetest\minetest.conf
Linux: ~/.minetest/minetest.conf
OS X: ~/Library/Application Support/minetest.conf
Command-line options:
---------------------
- Use --help
Compiling on GNU/Linux:
-----------------------
Install dependencies. Here's an example for Debian/Ubuntu:
$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev
Download source, extract (this is the URL to the latest of source repository, which might not work at all times):
$ wget https://bitbucket.org/celeron55/minetest/get/tip.tar.gz
$ tar xf tip.tar.gz
$ cd minetest
Build a version that runs directly from the source directory:
$ cmake . -DRUN_IN_PLACE=1
$ make -j2
Run it:
$ cd bin
$ ./minetest
- Use cmake . -LH to see all CMake options and their current state
- If you want to install it system-wide (or are making a distribution package), you will want to use -DRUN_IN_PLACE=0
- You can build a bare server or a bare client by specifying -DBUILD_CLIENT=0 or -DBUILD_SERVER=0
- You can select between Release and Debug build by -DCMAKE_BUILD_TYPE=<Debug or Release>
- Note that the Debug build is considerably slower
Compiling on Windows:
---------------------
- You need:
* CMake:
http://www.cmake.org/cmake/resources/software.html
* MinGW or Visual Studio
http://www.mingw.org/
http://msdn.microsoft.com/en-us/vstudio/default
* Irrlicht SDK 1.7:
http://irrlicht.sourceforge.net/downloads.html
* Zlib headers (zlib125.zip)
http://www.winimage.com/zLibDll/index.html
* Zlib library (zlibwapi.lib and zlibwapi.dll from zlib125dll.zip):
http://www.winimage.com/zLibDll/index.html
* And, of course, Minetest-c55:
http://celeron.55.lt/~celeron55/minetest/download
- Steps:
- Select a directory called DIR hereafter in which you will operate.
- Make sure you have CMake and a compiler installed.
- Download all the other stuff to DIR and extract them into there. All those
packages contain a nice base directory in them, which should end up being
the direct subdirectories of DIR.
- You will end up with a directory structure like this (+=dir, -=file):
-----------------
+ DIR
- zlib-1.2.5.tar.gz
- zlib125dll.zip
- irrlicht-1.7.1.zip
- 110214175330.zip (or whatever, this is the minetest source)
+ zlib-1.2.5
- zlib.h
+ win32
...
+ zlib125dll
- readme.txt
+ dll32
...
+ irrlicht-1.7.1
+ lib
+ include
...
+ minetest
+ src
+ doc
- CMakeLists.txt
...
-----------------
- Start up the CMake GUI
- Select "Browse Source..." and select DIR/minetest
- Now, if using MSVC:
- Select "Browse Build..." and select DIR/minetest-build
- Else if using MinGW:
- Select "Browse Build..." and select DIR/minetest
- Select "Configure"
- Select your compiler
- It will warn about missing stuff, ignore that at this point. (later don't)
- Make sure the configuration is as follows
(note that the versions may differ for you):
-----------------
BUILD_CLIENT [X]
BUILD_SERVER [ ]
CMAKE_BUILD_TYPE Release
CMAKE_INSTALL_PREFIX DIR/minetest-install
IRRLICHT_SOURCE_DIR DIR/irrlicht-1.7.1
RUN_IN_PLACE [X]
WARN_ALL [ ]
ZLIB_DLL DIR/zlib125dll/dll32/zlibwapi.dll
ZLIB_INCLUDE_DIR DIR/zlib-1.2.5
ZLIB_LIBRARIES DIR/zlib125dll/dll32/zlibwapi.lib
-----------------
- Hit "Configure"
- Hit "Generate"
If using MSVC:
- Open the generated minetest.sln
- The project defaults to the "Debug" configuration. Make very sure to
select "Release", unless you want to debug some stuff (it's slower)
- Build the ALL_BUILD project
- Build the INSTALL project
- You should now have a working game with the executable in
DIR/minetest-install/bin/minetest.exe
- Additionally you may create a zip package by building the PACKAGE
project.
If using MinGW:
- Using the command line, browse to the build directory and run 'make'
(or mingw32-make or whatever it happens to be)
- You should now have a working game with the executable in
DIR/minetest/bin/minetest.exe
License of Minetest-c55
-----------------------
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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.
Irrlicht
---------------
This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/
The Irrlicht Engine License
Copyright © 2002-2005 Nikolaus Gebhardt
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
JThread
---------------
This program uses the JThread library. License for JThread follows:
Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

@ -1,9 +0,0 @@
#!/bin/sh
for i in `grep -L 'This program is free software' src/*.{h,cpp}`
do
cat licensecomment.txt > tempfile
cat $i >> tempfile
cp tempfile $i
done
rm tempfile

BIN
data/gravel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

BIN
data/item_fence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
data/mossycobble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

BIN
data/treeprop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -3,7 +3,12 @@ Minetest-c55 changelog
This should contain all the major changes. This should contain all the major changes.
For minor stuff, refer to the commit log of the repository. For minor stuff, refer to the commit log of the repository.
X: 2011-06-02:
- Password crash on windows fixed
- Optimized server CPU usage a lot
- Furnaces now work also while players are not near to them
2011-05-29:
- Optimized smooth lighting - Optimized smooth lighting
- A number of small fixes - A number of small fixes
- Added clouds and simple skyboxes - Added clouds and simple skyboxes
@ -13,6 +18,10 @@ X:
- Slightly updated map format - Slightly updated map format
- Player passwords - Player passwords
- All textures first searched from texture_path - All textures first searched from texture_path
- Map directory ("map") has been renamed to "world" (just rename it to load an old world)
- Mouse inversion (invert_mouse)
- Grass doesn't grow immediately anymore
- Fence added
2011-04-24: 2011-04-24:
- Smooth lighting with simple ambient occlusion - Smooth lighting with simple ambient occlusion

89
doc/mapformat.txt Normal file

@ -0,0 +1,89 @@
I'll try to quickly document the newest block format in here (might contain
errors). Refer to the mapgen or minetestmapper script for the directory
structure and file naming. There are two sector namings possible,
sector/XXXXZZZZ and sector/XXX/ZZZ.
There also exists files map_meta.txt and chunk_meta, that are used by the
generator. If they are not found or invalid, the generator will currently
behave quite strangely.
The MapBlock file format (sectors2/XXX/ZZZ/YYYY):
-------------------------------------------------
NOTE: Byte order is MSB first.
u8 version
- map format version number, this one is version 17
u8 flags
- Flag bitmasks:
- 0x01: is_underground: Should be set to 0 if there will be no light
obstructions above the block. If/when sunlight of a block is updated and
there is no block above it, this value is checked for determining whether
sunlight comes from the top.
- 0x02: day_night_differs: Whether the lighting of the block is different on
day and night. Only blocks that have this bit set are updated when day
transforms to night.
- 0x04: lighting_expired: If true, lighting is invalid and should be updated.
If you can't calculate lighting in your generator properly, you could try
setting this 1 to everything and setting the uppermost block in every
sector as is_underground=0. I am quite sure it doesn't work properly,
though.
zlib-compressed map data:
- content:
u8[4096]: content types
u8[4096]: param1 values
u8[4096]: param2 values
zlib-compressed node metadata
- content:
u16 version (=1)
u16 count of metadata
foreach count:
u16 position (= p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X)
u16 type_id
u16 content_size
u8[content_size] misc. stuff contained in the metadata
u16 mapblockobject_count
- always write as 0.
- if read != 0, just fail.
foreach mapblockobject_count:
- deprecated, should not be used. Length of this data can only be known by
properly parsing it. Just hope not to run into any of this.
u8 static object version:
- currently 0
u16 static_object_count
foreach static_object_count:
u8 type (object type-id)
s32 pos_x * 1000
s32 pos_y * 1000
s32 pos_z * 1000
u16 data_size
u8[data_size] data
u32 timestamp
- Timestamp when last saved, as seconds from starting the game.
- 0xffffffff = invalid/unknown timestamp, nothing will be done with the time
difference when loaded (recommended)
Node metadata format:
---------------------
Sign metadata:
u16 string_len
u8[string_len] string
Furnace metadata:
TBD
Chest metadata:
TBD
// END

72
doc/protocol.txt Normal file

@ -0,0 +1,72 @@
Minetest-c55 protocol (incomplete, early draft):
Updated 2011-06-18
A custom protocol over UDP.
Integers are big endian.
Refer to connection.{h,cpp} for further reference.
Initialization:
- A dummy reliable packet with peer_id=PEER_ID_INEXISTENT=0 is sent to the server:
- Actually this can be sent without the reliable packet header, too, i guess,
but the sequence number in the header allows the sender to re-send the
packet without accidentally getting a double initialization.
- Packet content:
# Basic header
u32 protocol_id = PROTOCOL_ID = 0x4f457403
u16 sender_peer_id = PEER_ID_INEXISTENT = 0
u8 channel = 0
# Reliable packet header
u8 type = TYPE_RELIABLE = 3
u16 seqnum = SEQNUM_INITIAL = 65500
# Original packet header
u8 type = TYPE_ORIGINAL = 1
# And no actual payload.
- Server responds with something like this:
- Packet content:
# Basic header
u32 protocol_id = PROTOCOL_ID = 0x4f457403
u16 sender_peer_id = PEER_ID_INEXISTENT = 0
u8 channel = 0
# Reliable packet header
u8 type = TYPE_RELIABLE = 3
u16 seqnum = SEQNUM_INITIAL = 65500
# Control packet header
u8 type = TYPE_CONTROL = 0
u8 controltype = CONTROLTYPE_SET_PEER_ID = 1
u16 peer_id_new = assigned peer id to client (other than 0 or 1)
- Then the connection can be disconnected by sending:
- Packet content:
# Basic header
u32 protocol_id = PROTOCOL_ID = 0x4f457403
u16 sender_peer_id = whatever was gotten in CONTROLTYPE_SET_PEER_ID
u8 channel = 0
# Control packet header
u8 type = TYPE_CONTROL = 0
u8 controltype = CONTROLTYPE_DISCO = 2
- Here's a quick untested connect-disconnect done in PHP:
# host: ip of server (use gethostbyname(hostname) to get from a dns name)
# port: port of server
function check_if_minetestserver_up($host, $port)
{
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$timeout = array("sec" => 1, "usec" => 0);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
$buf = "\x4f\x45\x74\x03\x00\x00\x00\x03\xff\xdc\x01";
socket_sendto($socket, $buf, strlen($buf), 0, $host, $port);
$buf = socket_read($socket, 1000);
if($buf != "")
{
# We got a reply! read the peer id from it.
$peer_id = substr($buf, 9, 2);
# Disconnect
$buf = "\x4f\x45\x74\x03".$peer_id."\x00\x00\x02";
socket_sendto($socket, $buf, strlen($buf), 0, $host, $port);
socket_close($socket);
return true;
}
return false;
}

280
genmap.py

@ -6,19 +6,42 @@ import struct
import random import random
import os import os
import sys import sys
import zlib
import array
from pnoise import pnoise from pnoise import pnoise
""" # Old directory format:
Map format: # world/sectors/XXXXZZZZ/YYYY
map/sectors/XXXXZZZZ/YYYY # XXXX,YYYY,ZZZZ = coordinates in hexadecimal
# fffe = -2
# ffff = -1
# 0000 = 0
# 0001 = 1
#
# New directory format:
# world/sectors2/XXX/ZZZ/YYYY
# XXX,YYYY,ZZZ = coordinates in hexadecimal
# fffe = -2
# ffff = -1
# 0000 = 0
# 0001 = 1
# ffe = -2
# fff = -1
# 000 = 0
# 001 = 1
#
# For more proper file format documentation, refer to mapformat.txt
# For node type documentation, refer to mapnode.h
# NodeMetadata documentation is not complete, refer to nodemeta.cpp
#
XXXX,YYYY,ZZZZ = coordinates in hexadecimal # Seed for generating terrain
SEED = 0
fffe = -2 # 0=old, 1=new
ffff = -1 SECTOR_DIR_FORMAT = 1
0000 = 0
0001 = 1 mapdir = "world"
"""
def to4h(i): def to4h(i):
s = ""; s = "";
@ -28,64 +51,221 @@ def to4h(i):
s += '{0:1x}'.format((i>>0) & 0x000f) s += '{0:1x}'.format((i>>0) & 0x000f)
return s return s
def getrand(): def to3h(i):
s = "";
s += '{0:1x}'.format((i>>8) & 0x000f)
s += '{0:1x}'.format((i>>4) & 0x000f)
s += '{0:1x}'.format((i>>0) & 0x000f)
return s
def get_sector_dir(px, pz):
global SECTOR_DIR_FORMAT
if SECTOR_DIR_FORMAT == 0:
return "/sectors/"+to4h(px)+to4h(pz)
elif SECTOR_DIR_FORMAT == 1:
return "/sectors2/"+to3h(px)+"/"+to3h(pz)
else:
assert(0)
def getrand_air_stone():
i = random.randrange(0,2) i = random.randrange(0,2)
if i==0: if i==0:
return 0 return 0
return 254 return 254
def writeblock(mapdir, px,py,pz, version): # 3-dimensional vector (position)
sectordir = mapdir + "/sectors/" + to4h(px) + to4h(pz) class v3:
def __init__(self, x=0, y=0, z=0):
self.X = x
self.Y = y
self.Z = z
class NodeMeta:
def __init__(self, type_id, data):
self.type_id = type_id
self.data = data
class StaticObject:
def __init__(self):
self.type_id = 0
self.data = ""
def ser_u16(i):
return chr((i>>8)&0xff) + chr((i>>0)&0xff)
def ser_u32(i):
return (chr((i>>24)&0xff) + chr((i>>16)&0xff)
+ chr((i>>8)&0xff) + chr((i>>0)&0xff))
# A 16x16x16 chunk of map
class MapBlock:
def __init__(self):
self.content = array.array('B')
self.param1 = array.array('B')
self.param2 = array.array('B')
for i in range(16*16*16):
# Initialize to air
self.content.append(254)
# Full light on sunlight, none when no sunlight
self.param1.append(15)
# No additional parameters
self.param2.append(0)
# key = v3 pos
# value = NodeMeta
self.nodemeta = {}
# key = v3 pos
# value = StaticObject
self.static_objects = {}
def set_content(self, v3, b):
self.content[v3.Z*16*16+v3.Y*16+v3.X] = b
def set_param1(self, v3, b):
self.param1[v3.Z*16*16+v3.Y*16+v3.X] = b
def set_param2(self, v3, b):
self.param2[v3.Z*16*16+v3.Y*16+v3.X] = b
# Get data for serialization. Returns a string.
def serialize_data(self):
s = ""
for i in range(16*16*16):
s += chr(self.content[i])
for i in range(16*16*16):
s += chr(self.param1[i])
for i in range(16*16*16):
s += chr(self.param2[i])
return s
def serialize_nodemeta(self):
s = ""
s += ser_u16(1)
s += ser_u16(len(self.nodemeta))
for pos, meta in self.nodemeta.items():
pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
s += ser_u16(pos_i)
s += ser_u16(meta.type_id)
s += ser_u16(len(meta.data))
s += meta.data
return s
def serialize_staticobj(self):
s = ""
s += chr(0)
s += ser_u16(len(self.static_objects))
for pos, obj in self.static_objects.items():
pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
s += ser_s32(pos.X*1000)
s += ser_s32(pos.Y*1000)
s += ser_s32(pos.Z*1000)
s += ser_u16(obj.type_id)
s += ser_u16(len(obj.data))
s += obj.data
return s
def writeblock(mapdir, px,py,pz, block):
sectordir = mapdir + get_sector_dir(px, pz);
try: try:
os.makedirs(sectordir) os.makedirs(sectordir)
except OSError: except OSError:
pass pass
path = sectordir+"/"+to4h(py)
print("writing block file "+path)
f = open(sectordir+"/"+to4h(py), "wb") f = open(sectordir+"/"+to4h(py), "wb")
if version == 0: if f == None:
# version return
f.write(struct.pack('B', 0))
# is_underground
f.write(struct.pack('B', 0))
elif version == 2:
# version
f.write(struct.pack('B', 2))
# is_underground
f.write(struct.pack('B', 0))
for z in range(0,16): # version
for y in range(0,16): version = 17
for x in range(0,16): f.write(struct.pack('B', version))
b = 254
r = 20.0*pnoise((px*16+x)/100.,(pz*16+z)/100.,0) # flags
r += 5.0*pnoise((px*16+x)/25.,(pz*16+z)/25.,0) # 0x01=is_undg, 0x02=dn_diff, 0x04=lighting_expired
#print("r="+str(r)) flags = 0 + 0x02 + 0x04
y1 = py*16+y f.write(struct.pack('B', flags))
if y1 <= r-3:
b = 0 #stone # data
elif y1 <= r: c_obj = zlib.compressobj()
b = 1 #grass c_obj.compress(block.serialize_data())
elif y1 <= 1: f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
b = 9 #water f.write(c_obj.flush())
if version == 0:
# Material content # node metadata
f.write(struct.pack('B', b)) c_obj = zlib.compressobj()
elif version == 2: c_obj.compress(block.serialize_nodemeta())
# Material content f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
f.write(struct.pack('B', b)) f.write(c_obj.flush())
# Brightness
f.write(struct.pack('B', 15)) # mapblockobject count
f.write(ser_u16(0))
# static objects
f.write(block.serialize_staticobj())
# timestamp
f.write(ser_u32(0xffffffff))
f.close() f.close()
mapdir = "map" for z0 in range(-1,3):
for x0 in range(-1,3):
for y0 in range(-1,3):
print("generating block "+str(x0)+","+str(y0)+","+str(z0))
#v3 blockp = v3(x0,y0,z0)
for z in range(-2,3): # Create a MapBlock
for y in range(-1,2): block = MapBlock()
for x in range(-2,3):
print("generating block "+str(x)+","+str(y)+","+str(z)) # Generate stuff in it
writeblock(mapdir, x,y,z, 0) for z in range(0,16):
for x in range(0,16):
h = 20.0*pnoise((x0*16+x)/100.,(z0*16+z)/100.,SEED+0)
h += 5.0*pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+0)
if pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+92412) > 0.05:
h += 10
#print("r="+str(r))
# This enables comparison by ==
h = int(h)
for y in range(0,16):
p = v3(x,y,z)
b = 254
y1 = y0*16+y
if y1 <= h-3:
b = 0 #stone
elif y1 <= h and y1 <= 0:
b = 8 #mud
elif y1 == h:
b = 1 #grass
elif y1 < h:
b = 8 #mud
elif y1 <= 1:
b = 9 #water
# Material content
block.set_content(p, b)
# Place a sign at the center at surface level.
# Placing a sign means placing the sign node and
# adding node metadata to the mapblock.
if x == 8 and z == 8 and y0*16 <= h-1 and (y0+1)*16-1 > h:
p = v3(8,h+1-y0*16,8)
# 14 = Sign
content_type = 14
block.set_content(p, content_type)
# This places the sign to the bottom of the cube.
# Working values: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20
block.set_param2(p, 0x08)
# Then add metadata to hold the text of the sign
s = "Hello at sector ("+str(x0)+","+str(z0)+")"
meta = NodeMeta(content_type, ser_u16(len(s))+s)
block.nodemeta[p] = meta
# Write it on disk
writeblock(mapdir, x0,y0,z0, block)
#END #END

@ -42,6 +42,8 @@
#keymap_special1 = KEY_KEY_E #keymap_special1 = KEY_KEY_E
#keymap_print_debug_stacks = KEY_KEY_P #keymap_print_debug_stacks = KEY_KEY_P
#invert_mouse = false
# The desired FPS # The desired FPS
#wanted_fps = 30 #wanted_fps = 30
@ -56,6 +58,12 @@
# Whether to fog out the end of the visible area # Whether to fog out the end of the visible area
#enable_fog = true #enable_fog = true
# Enable/disable clouds
#enable_clouds = true
# Experimental
#enable_farmesh = false
# Enable a bit lower water surface; disable for speed # Enable a bit lower water surface; disable for speed
#new_style_water = true #new_style_water = true
@ -90,10 +98,6 @@
# Server side stuff # Server side stuff
# #
# Set to true to enable experimental features
# (varies from version to version, see wiki)
#enable_experimental = false
# Map directory (everything in the world is stored here) # Map directory (everything in the world is stored here)
#map-dir = /home/palle/custom_map #map-dir = /home/palle/custom_map
@ -102,9 +106,21 @@
#enable_damage = false #enable_damage = false
#default_password =
# Available privileges: build, teleport, settime, privs, shout
#default_privs = build, shout
# Gives some stuff to players at the beginning # Gives some stuff to players at the beginning
#give_initial_stuff = false #give_initial_stuff = false
# Set to true to enable experimental features
# (varies from version to version, see wiki)
#enable_experimental = false
# Profiler data print interval. 0 = disable.
#profiler_print_interval = 10
# Player and object positions are sent at intervals specified by this # Player and object positions are sent at intervals specified by this
#objectdata_inverval = 0.2 #objectdata_inverval = 0.2

@ -61,6 +61,13 @@ configure_file(
) )
set(common_SRCS set(common_SRCS
mapgen.cpp
content_inventory.cpp
content_nodemeta.cpp
content_craft.cpp
content_mapblock.cpp
content_mapnode.cpp
auth.cpp
collision.cpp collision.cpp
nodemetadata.cpp nodemetadata.cpp
serverobject.cpp serverobject.cpp
@ -95,10 +102,11 @@ set(common_SRCS
# Client sources # Client sources
set(minetest_SRCS set(minetest_SRCS
${common_SRCS} ${common_SRCS}
mapblock_mesh.cpp
farmesh.cpp
keycode.cpp keycode.cpp
clouds.cpp clouds.cpp
clientobject.cpp clientobject.cpp
guiFurnaceMenu.cpp
guiMainMenu.cpp guiMainMenu.cpp
guiKeyChangeMenu.cpp guiKeyChangeMenu.cpp
guiMessageMenu.cpp guiMessageMenu.cpp
@ -125,6 +133,7 @@ include_directories(
${CMAKE_BUILD_TYPE} ${CMAKE_BUILD_TYPE}
${PNG_INCLUDE_DIR} ${PNG_INCLUDE_DIR}
"${PROJECT_SOURCE_DIR}/jthread" "${PROJECT_SOURCE_DIR}/jthread"
"${PROJECT_SOURCE_DIR}/sqlite"
) )
set(EXECUTABLE_OUTPUT_PATH ../bin) set(EXECUTABLE_OUTPUT_PATH ../bin)
@ -143,6 +152,7 @@ if(BUILD_CLIENT)
${PLATFORM_LIBS} ${PLATFORM_LIBS}
${CLIENT_PLATFORM_LIBS} ${CLIENT_PLATFORM_LIBS}
jthread jthread
sqlite3
) )
endif(BUILD_CLIENT) endif(BUILD_CLIENT)
@ -153,6 +163,7 @@ if(BUILD_SERVER)
${ZLIB_LIBRARIES} ${ZLIB_LIBRARIES}
${PLATFORM_LIBS} ${PLATFORM_LIBS}
jthread jthread
sqlite3
) )
endif(BUILD_SERVER) endif(BUILD_SERVER)
@ -181,21 +192,22 @@ else()
# Probably GCC # Probably GCC
if(WARN_ALL) if(WARN_ALL)
set(WARNING_FLAGS "-Wall") set(RELEASE_WARNING_FLAGS "-Wall")
else() else()
set(WARNING_FLAGS "") set(RELEASE_WARNING_FLAGS "")
endif() endif()
if(NOT APPLE) if(NOT APPLE)
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-unused-but-set-variable") set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-unused-but-set-variable")
endif() endif()
if(APPLE) if(APPLE)
set(CMAKE_OSX_ARCHITECTURES i386 CACHE STRING "do not build for 64-bit" FORCE) set(CMAKE_OSX_ARCHITECTURES i386 CACHE STRING "do not build for 64-bit" FORCE)
set(ARCH i386) set(ARCH i386)
endif() endif()
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${WARNING_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall") set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS}")
if(USE_GPROF) if(USE_GPROF)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
@ -242,5 +254,6 @@ endif(BUILD_SERVER)
# Subdirectories # Subdirectories
add_subdirectory(jthread) add_subdirectory(jthread)
add_subdirectory(sqlite)
#end #end

264
src/auth.cpp Normal file

@ -0,0 +1,264 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "auth.h"
#include <fstream>
#include <jmutexautolock.h>
//#include "main.h" // for g_settings
#include <sstream>
#include "strfnd.h"
#include "debug.h"
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs)
{
std::ostringstream os(std::ios_base::binary);
if(privs & PRIV_BUILD)
os<<"build,";
if(privs & PRIV_TELEPORT)
os<<"teleport,";
if(privs & PRIV_SETTIME)
os<<"settime,";
if(privs & PRIV_PRIVS)
os<<"privs,";
if(privs & PRIV_SHOUT)
os<<"shout,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::string tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str)
{
u64 privs=0;
Strfnd f(str);
while(f.atend() == false)
{
std::string s = trim(f.next(","));
if(s == "build")
privs |= PRIV_BUILD;
else if(s == "teleport")
privs |= PRIV_TELEPORT;
else if(s == "settime")
privs |= PRIV_SETTIME;
else if(s == "privs")
privs |= PRIV_PRIVS;
else if(s == "shout")
privs |= PRIV_SHOUT;
else
return PRIV_INVALID;
}
return privs;
}
AuthManager::AuthManager(const std::string &authfilepath):
m_authfilepath(authfilepath),
m_modified(false)
{
m_mutex.Init();
try{
load();
}
catch(SerializationError &e)
{
dstream<<"WARNING: AuthManager: creating "
<<m_authfilepath<<std::endl;
}
}
AuthManager::~AuthManager()
{
save();
}
void AuthManager::load()
{
JMutexAutoLock lock(m_mutex);
dstream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
if(is.good() == false)
{
dstream<<"AuthManager: failed loading from "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::load(): Couldn't open file");
}
for(;;)
{
if(is.eof() || is.good() == false)
break;
// Read a line
std::string line;
std::getline(is, line, '\n');
std::istringstream iss(line);
// Read name
std::string name;
std::getline(iss, name, ':');
// Read password
std::string pwd;
std::getline(iss, pwd, ':');
// Read privileges
std::string stringprivs;
std::getline(iss, stringprivs, ':');
u64 privs = stringToPrivs(stringprivs);
// Store it
AuthData ad;
ad.pwd = pwd;
ad.privs = privs;
m_authdata[name] = ad;
}
m_modified = false;
}
void AuthManager::save()
{
JMutexAutoLock lock(m_mutex);
dstream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
if(os.good() == false)
{
dstream<<"AuthManager: failed saving to "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::save(): Couldn't open file");
}
for(core::map<std::string, AuthData>::Iterator
i = m_authdata.getIterator();
i.atEnd()==false; i++)
{
std::string name = i.getNode()->getKey();
if(name == "")
continue;
AuthData ad = i.getNode()->getValue();
os<<name<<":"<<ad.pwd<<":"<<privsToString(ad.privs)<<"\n";
}
m_modified = false;
}
bool AuthManager::exists(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
return false;
return true;
}
void AuthManager::set(const std::string &username, AuthData ad)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = ad;
m_modified = true;
}
void AuthManager::add(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = AuthData();
m_modified = true;
}
std::string AuthManager::getPassword(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().pwd;
}
void AuthManager::setPassword(const std::string &username,
const std::string &password)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.pwd = password;
n->setValue(ad);
m_modified = true;
}
u64 AuthManager::getPrivs(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().privs;
}
void AuthManager::setPrivs(const std::string &username, u64 privs)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.privs = privs;
n->setValue(ad);
m_modified = true;
}
bool AuthManager::isModified()
{
JMutexAutoLock lock(m_mutex);
return m_modified;
}

101
src/auth.h Normal file

@ -0,0 +1,101 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 AUTH_HEADER
#define AUTH_HEADER
#include <string>
#include <jthread.h>
#include <jmutex.h>
#include "common_irrlicht.h"
#include "exceptions.h"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_BUILD = 1; // Can build - i.e. modify the world
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_BUILD|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str);
struct AuthData
{
std::string pwd;
u64 privs;
AuthData():
privs(PRIV_DEFAULT)
{
}
};
class AuthNotFoundException : public BaseException
{
public:
AuthNotFoundException(const char *s):
BaseException(s)
{}
};
class AuthManager
{
public:
AuthManager(const std::string &authfilepath);
~AuthManager();
void load();
void save();
bool exists(const std::string &username);
void set(const std::string &username, AuthData ad);
void add(const std::string &username);
std::string getPassword(const std::string &username);
void setPassword(const std::string &username,
const std::string &password);
u64 getPrivs(const std::string &username);
void setPrivs(const std::string &username, u64 privs);
bool isModified();
private:
JMutex m_mutex;
std::string m_authfilepath;
core::map<std::string, AuthData> m_authdata;
bool m_modified;
};
#endif

@ -72,8 +72,9 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_
for (j = 0; (j < i + 1); j++) for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]]; ret += base64_chars[char_array_4[j]];
while((i++ < 3)) // Don't pad it with =
ret += '='; /*while((i++ < 3))
ret += '=';*/
} }

@ -39,10 +39,12 @@ void * MeshUpdateThread::Thread()
QueuedMeshUpdate *q = m_queue_in.pop(); QueuedMeshUpdate *q = m_queue_in.pop();
if(q == NULL) if(q == NULL)
{ {
sleep_ms(50); sleep_ms(3);
continue; continue;
} }
ScopeProfiler sp(&g_profiler, "mesh make");
scene::SMesh *mesh_new = NULL; scene::SMesh *mesh_new = NULL;
mesh_new = makeMapBlockMesh(q->data); mesh_new = makeMapBlockMesh(q->data);
@ -218,12 +220,12 @@ void Client::step(float dtime)
g_settings.getFloat("client_delete_unused_sectors_timeout"); g_settings.getFloat("client_delete_unused_sectors_timeout");
// Delete sector blocks // Delete sector blocks
/*u32 num = m_env.getMap().deleteUnusedSectors /*u32 num = m_env.getMap().unloadUnusedData
(delete_unused_sectors_timeout, (delete_unused_sectors_timeout,
true, &deleted_blocks);*/ true, &deleted_blocks);*/
// Delete whole sectors // Delete whole sectors
u32 num = m_env.getMap().deleteUnusedSectors u32 num = m_env.getMap().unloadUnusedData
(delete_unused_sectors_timeout, (delete_unused_sectors_timeout,
false, &deleted_blocks); false, &deleted_blocks);
@ -306,8 +308,14 @@ void Client::step(float dtime)
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE); SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
writeU16(&data[0], TOSERVER_INIT); writeU16(&data[0], TOSERVER_INIT);
writeU8(&data[2], SER_FMT_VER_HIGHEST); writeU8(&data[2], SER_FMT_VER_HIGHEST);
memset((char*)&data[3], 0, PLAYERNAME_SIZE); memset((char*)&data[3], 0, PLAYERNAME_SIZE);
snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName()); snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
/*dstream<<"Client: password hash is \""<<m_password<<"\""
<<std::endl;*/
memset((char*)&data[23], 0, PASSWORD_SIZE);
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str()); snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
// Send as unreliable // Send as unreliable
@ -610,6 +618,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
// to be processed even if the serialisation format has // to be processed even if the serialisation format has
// not been agreed yet, the same as TOCLIENT_INIT. // not been agreed yet, the same as TOCLIENT_INIT.
m_access_denied = true; m_access_denied = true;
m_access_denied_reason = L"Unknown";
if(datasize >= 4)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
m_access_denied_reason = deSerializeWideString(is);
}
return; return;
} }
@ -780,7 +795,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
*/ */
//m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
/*
Add it to mesh update queue and set it to be acknowledged after update.
*/
addUpdateMeshTaskWithEdge(p, true); addUpdateMeshTaskWithEdge(p, true);
} }
else if(command == TOCLIENT_PLAYERPOS) else if(command == TOCLIENT_PLAYERPOS)

@ -385,6 +385,11 @@ public:
return m_access_denied; return m_access_denied;
} }
inline std::wstring accessDeniedReason()
{
return m_access_denied_reason;
}
private: private:
// Virtual methods from con::PeerHandler // Virtual methods from con::PeerHandler
@ -440,10 +445,13 @@ private:
std::string m_password; std::string m_password;
bool m_access_denied; bool m_access_denied;
std::wstring m_access_denied_reason;
InventoryContext m_inventory_context; InventoryContext m_inventory_context;
Queue<ClientEvent> m_client_event_queue; Queue<ClientEvent> m_client_event_queue;
friend class FarMesh;
}; };
#endif // !SERVER #endif // !SERVER

@ -24,6 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PROTOCOL_ID 0x4f457403 #define PROTOCOL_ID 0x4f457403
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
// base64-encoded SHA-1 (27+\0).
enum ToClientCommand enum ToClientCommand
{ {
TOCLIENT_INIT = 0x10, TOCLIENT_INIT = 0x10,
@ -154,6 +157,8 @@ enum ToClientCommand
TOCLIENT_ACCESS_DENIED = 0x35, TOCLIENT_ACCESS_DENIED = 0x35,
/* /*
u16 command u16 command
u16 reason_length
wstring reason
*/ */
}; };

@ -58,8 +58,8 @@ void Clouds::OnRegisterSceneNode()
{ {
if(IsVisible) if(IsVisible)
{ {
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
//SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); //SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
} }
ISceneNode::OnRegisterSceneNode(); ISceneNode::OnRegisterSceneNode();

586
src/content_craft.cpp Normal file

@ -0,0 +1,586 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "content_craft.h"
#include "inventory.h"
#include "content_mapnode.h"
#include "player.h"
/*
items: actually *items[9]
return value: allocates a new item, or returns NULL.
*/
InventoryItem *craft_get_result(InventoryItem **items)
{
// Wood
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_WOOD, 4);
}
}
// Stick
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
if(checkItemCombination(items, specs))
{
return new CraftItem("Stick", 4);
}
}
// Fence
{
ItemSpec specs[9];
specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_FENCE, 2);
}
}
// Sign
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
//return new MapBlockObjectItem("Sign");
return new MaterialItem(CONTENT_SIGN_WALL, 1);
}
}
// Torch
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_TORCH, 4);
}
}
// Wooden pick
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("WPick", 0);
}
}
// Stone pick
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("STPick", 0);
}
}
// Steel pick
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("SteelPick", 0);
}
}
// Mese pick
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("MesePick", 0);
}
}
// Wooden shovel
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("WShovel", 0);
}
}
// Stone shovel
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("STShovel", 0);
}
}
// Steel shovel
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("SteelShovel", 0);
}
}
// Wooden axe
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("WAxe", 0);
}
}
// Stone axe
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("STAxe", 0);
}
}
// Steel axe
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("SteelAxe", 0);
}
}
// Wooden sword
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("WSword", 0);
}
}
// Stone sword
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("STSword", 0);
}
}
// Steel sword
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
return new ToolItem("SteelSword", 0);
}
}
// Rail
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[1] = ItemSpec(ITEM_CRAFT, "Stick");
specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_RAIL, 15);
}
}
// Chest
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_CHEST, 1);
}
}
// Furnace
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_FURNACE, 1);
}
}
// Steel block
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_STEEL, 1);
}
}
// Sandstone
{
ItemSpec specs[9];
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_SANDSTONE, 1);
}
}
// Clay
{
ItemSpec specs[9];
specs[3] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
specs[4] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
specs[6] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
specs[7] = ItemSpec(ITEM_CRAFT, "lump_of_clay");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_CLAY, 1);
}
}
// Brick
{
ItemSpec specs[9];
specs[3] = ItemSpec(ITEM_CRAFT, "clay_brick");
specs[4] = ItemSpec(ITEM_CRAFT, "clay_brick");
specs[6] = ItemSpec(ITEM_CRAFT, "clay_brick");
specs[7] = ItemSpec(ITEM_CRAFT, "clay_brick");
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_BRICK, 1);
}
}
// Paper
{
ItemSpec specs[9];
specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS);
if(checkItemCombination(items, specs))
{
return new CraftItem("paper", 1);
}
}
// Book
{
ItemSpec specs[9];
specs[1] = ItemSpec(ITEM_CRAFT, "paper");
specs[4] = ItemSpec(ITEM_CRAFT, "paper");
specs[7] = ItemSpec(ITEM_CRAFT, "paper");
if(checkItemCombination(items, specs))
{
return new CraftItem("book", 1);
}
}
// Book shelf
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[3] = ItemSpec(ITEM_CRAFT, "book");
specs[4] = ItemSpec(ITEM_CRAFT, "book");
specs[5] = ItemSpec(ITEM_CRAFT, "book");
specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
if(checkItemCombination(items, specs))
{
return new MaterialItem(CONTENT_BOOKSHELF, 1);
}
}
return NULL;
}
void craft_set_creative_inventory(Player *player)
{
player->resetInventory();
// Give some good tools
{
InventoryItem *item = new ToolItem("MesePick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("SteelPick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("SteelAxe", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("SteelShovel", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
/*
Give materials
*/
// CONTENT_IGNORE-terminated list
u8 material_items[] = {
CONTENT_TORCH,
CONTENT_COBBLE,
CONTENT_MUD,
CONTENT_STONE,
CONTENT_SAND,
CONTENT_SANDSTONE,
CONTENT_CLAY,
CONTENT_BRICK,
CONTENT_TREE,
CONTENT_LEAVES,
CONTENT_CACTUS,
CONTENT_PAPYRUS,
CONTENT_BOOKSHELF,
CONTENT_GLASS,
CONTENT_FENCE,
CONTENT_RAIL,
CONTENT_MESE,
CONTENT_WATERSOURCE,
CONTENT_CLOUD,
CONTENT_CHEST,
CONTENT_FURNACE,
CONTENT_SIGN_WALL,
CONTENT_IGNORE
};
u8 *mip = material_items;
for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
{
if(*mip == CONTENT_IGNORE)
break;
InventoryItem *item = new MaterialItem(*mip, 1);
player->inventory.addItem("main", item);
mip++;
}
#if 0
assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
// add torch first
InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
player->inventory.addItem("main", item);
// Then others
for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
{
// Skip some materials
if(i == CONTENT_WATER || i == CONTENT_TORCH
|| i == CONTENT_COALSTONE)
continue;
InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem("main", item);
}
#endif
/*// Sign
{
InventoryItem *item = new MapBlockObjectItem("Sign Example text");
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}*/
}
void craft_give_initial_stuff(Player *player)
{
{
InventoryItem *item = new ToolItem("SteelPick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("SteelAxe", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("SteelShovel", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
/*{
InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new CraftItem("Stick", 4);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("WPick", 32000);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("STPick", 32000);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}*/
/*// and some signs
for(u16 i=0; i<4; i++)
{
InventoryItem *item = new MapBlockObjectItem("Sign Example text");
bool r = player->inventory.addItem("main", item);
assert(r == true);
}*/
/*// Give some other stuff
{
InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
bool r = player->inventory.addItem("main", item);
assert(r == true);
}*/
}

@ -1,6 +1,6 @@
/* /*
Minetest-c55 Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -17,28 +17,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef GUIFURNACEMENU_HEADER #ifndef CONTENT_CRAFT_HEADER
#define GUIFURNACEMENU_HEADER #define CONTENT_CRAFT_HEADER
#include "guiInventoryMenu.h" class InventoryItem;
class Player;
class Client; /*
items: actually *items[9]
return value: allocates a new item, or returns NULL.
*/
InventoryItem *craft_get_result(InventoryItem **items);
class GUIFurnaceMenu : public GUIInventoryMenu void craft_set_creative_inventory(Player *player);
{
public:
GUIFurnaceMenu(
gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
v3s16 nodepos,
Client *client
);
private:
v3s16 m_nodepos; // Called when give_initial_stuff setting is used
Client *m_client; void craft_give_initial_stuff(Player *player);
};
#endif #endif

108
src/content_inventory.cpp Normal file

@ -0,0 +1,108 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "content_inventory.h"
#include "inventory.h"
#include "serverobject.h"
#include "content_mapnode.h"
bool item_material_is_cookable(u8 content)
{
if(content == CONTENT_TREE)
return true;
else if(content == CONTENT_COBBLE)
return true;
else if(content == CONTENT_SAND)
return true;
return false;
}
InventoryItem* item_material_create_cook_result(u8 content)
{
if(content == CONTENT_TREE)
return new CraftItem("lump_of_coal", 1);
else if(content == CONTENT_COBBLE)
return new MaterialItem(CONTENT_STONE, 1);
else if(content == CONTENT_SAND)
return new MaterialItem(CONTENT_GLASS, 1);
return NULL;
}
std::string item_craft_get_image_name(const std::string &subname)
{
if(subname == "Stick")
return "stick.png";
else if(subname == "paper")
return "paper.png";
else if(subname == "book")
return "book.png";
else if(subname == "lump_of_coal")
return "lump_of_coal.png";
else if(subname == "lump_of_iron")
return "lump_of_iron.png";
else if(subname == "lump_of_clay")
return "lump_of_clay.png";
else if(subname == "steel_ingot")
return "steel_ingot.png";
else if(subname == "clay_brick")
return "clay_brick.png";
else if(subname == "rat")
return "rat.png";
else
return "cloud.png"; // just something
}
ServerActiveObject* item_craft_create_object(const std::string &subname,
ServerEnvironment *env, u16 id, v3f pos)
{
if(subname == "rat")
{
ServerActiveObject *obj = new RatSAO(env, id, pos);
return obj;
}
return NULL;
}
s16 item_craft_get_drop_count(const std::string &subname)
{
if(subname == "rat")
return 1;
return -1;
}
bool item_craft_is_cookable(const std::string &subname)
{
if(subname == "lump_of_iron" || subname == "lump_of_clay")
return true;
return false;
}
InventoryItem* item_craft_create_cook_result(const std::string &subname)
{
if(subname == "lump_of_iron")
return new CraftItem("steel_ingot", 1);
else if(subname == "lump_of_clay")
return new CraftItem("clay_brick", 1);
return NULL;
}

41
src/content_inventory.h Normal file

@ -0,0 +1,41 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 CONTENT_INVENTORY_HEADER
#define CONTENT_INVENTORY_HEADER
#include "common_irrlicht.h" // For u8, s16
#include <string>
class InventoryItem;
class ServerActiveObject;
class ServerEnvironment;
bool item_material_is_cookable(u8 content);
InventoryItem* item_material_create_cook_result(u8 content);
std::string item_craft_get_image_name(const std::string &subname);
ServerActiveObject* item_craft_create_object(const std::string &subname,
ServerEnvironment *env, u16 id, v3f pos);
s16 item_craft_get_drop_count(const std::string &subname);
bool item_craft_is_cookable(const std::string &subname);
InventoryItem* item_craft_create_cook_result(const std::string &subname);
#endif

1076
src/content_mapblock.cpp Normal file

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
/* /*
Minetest-c55 Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -17,3 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef CONTENT_MAPBLOCK_HEADER
#define CONTENT_MAPBLOCK_HEADER
#ifndef SERVER
#include "mapblock_mesh.h"
#include "utility.h"
void mapblock_mesh_generate_special(MeshMakeData *data,
MeshCollector &collector);
#endif
#endif

450
src/content_mapnode.cpp Normal file

@ -0,0 +1,450 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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.
*/
// For g_settings
#include "main.h"
#include "content_mapnode.h"
#include "mapnode.h"
#include "content_nodemeta.h"
// TODO: Get rid of these and set up some attributes like toughness,
// fluffyness, and a funciton to calculate time and durability loss
// (and sound? and whatever else) from them
void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness);
void content_mapnode_init()
{
// Read some settings
bool new_style_water = g_settings.getBool("new_style_water");
bool new_style_leaves = g_settings.getBool("new_style_leaves");
bool invisible_stone = g_settings.getBool("invisible_stone");
u8 i;
ContentFeatures *f = NULL;
i = CONTENT_STONE;
f = &content_features(i);
f->setAllTextures("stone.png");
f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 1";
setStoneLikeDiggingProperties(f->digging_properties, 1.0);
if(invisible_stone)
f->solidness = 0; // For debugging, hides regular stone
i = CONTENT_GRASS;
f = &content_features(i);
f->setAllTextures("mud.png^grass_side.png");
f->setTexture(0, "grass.png");
f->setTexture(1, "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_GRASS_FOOTSTEPS;
f = &content_features(i);
f->setAllTextures("mud.png^grass_side.png");
f->setTexture(0, "grass_footsteps.png");
f->setTexture(1, "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_MUD;
f = &content_features(i);
f->setAllTextures("mud.png");
f->setInventoryTextureCube("mud.png", "mud.png", "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_SAND;
f = &content_features(i);
f->setAllTextures("sand.png");
f->setInventoryTextureCube("sand.png", "sand.png", "sand.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_GRAVEL;
f = &content_features(i);
f->setAllTextures("gravel.png");
f->setInventoryTextureCube("gravel.png", "gravel.png", "gravel.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.75);
i = CONTENT_SANDSTONE;
f = &content_features(i);
f->setAllTextures("sandstone.png");
f->setInventoryTextureCube("sandstone.png", "sandstone.png", "sandstone.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_SAND)+" 1";
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_CLAY;
f = &content_features(i);
f->setAllTextures("clay.png");
f->setInventoryTextureCube("clay.png", "clay.png", "clay.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("CraftItem lump_of_clay 4");
setDirtLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_BRICK;
f = &content_features(i);
f->setAllTextures("brick.png");
f->setInventoryTextureCube("brick.png", "brick.png", "brick.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("CraftItem clay_brick 4");
setStoneLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_TREE;
f = &content_features(i);
f->setAllTextures("tree.png");
f->setTexture(0, "tree_top.png");
f->setTexture(1, "tree_top.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setWoodLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_LEAVES;
f = &content_features(i);
f->light_propagates = true;
//f->param_type = CPT_MINERAL;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
if(new_style_leaves)
{
f->solidness = 0; // drawn separately, makes no faces
f->setInventoryTextureCube("leaves.png", "leaves.png", "leaves.png");
}
else
{
f->setAllTextures("[noalpha:leaves.png");
}
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setWoodLikeDiggingProperties(f->digging_properties, 0.15);
i = CONTENT_CACTUS;
f = &content_features(i);
f->setAllTextures("cactus_side.png");
f->setTexture(0, "cactus_top.png");
f->setTexture(1, "cactus_top.png");
f->setInventoryTextureCube("cactus_top.png", "cactus_side.png", "cactus_side.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setWoodLikeDiggingProperties(f->digging_properties, 0.75);
i = CONTENT_PAPYRUS;
f = &content_features(i);
f->setInventoryTexture("papyrus.png");
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
setWoodLikeDiggingProperties(f->digging_properties, 0.25);
i = CONTENT_BOOKSHELF;
f = &content_features(i);
f->setAllTextures("bookshelf.png");
f->setTexture(0, "wood.png");
f->setTexture(1, "wood.png");
// FIXME: setInventoryTextureCube() only cares for the first texture
f->setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png");
//f->setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
setWoodLikeDiggingProperties(f->digging_properties, 0.75);
i = CONTENT_GLASS;
f = &content_features(i);
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->setInventoryTextureCube("glass.png", "glass.png", "glass.png");
setWoodLikeDiggingProperties(f->digging_properties, 0.15);
i = CONTENT_FENCE;
f = &content_features(i);
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->air_equivalent = true; // grass grows underneath
f->setInventoryTexture("item_fence.png");
setWoodLikeDiggingProperties(f->digging_properties, 0.75);
i = CONTENT_RAIL;
f = &content_features(i);
f->setInventoryTexture("rail.png");
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->air_equivalent = true; // grass grows underneath
f->walkable = false;
setDirtLikeDiggingProperties(f->digging_properties, 0.75);
// Deprecated
i = CONTENT_COALSTONE;
f = &content_features(i);
//f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
f->setAllTextures("stone.png^mineral_coal.png");
f->is_ground_content = true;
setStoneLikeDiggingProperties(f->digging_properties, 1.5);
i = CONTENT_WOOD;
f = &content_features(i);
f->setAllTextures("wood.png");
f->setInventoryTextureCube("wood.png", "wood.png", "wood.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setWoodLikeDiggingProperties(f->digging_properties, 0.75);
i = CONTENT_MESE;
f = &content_features(i);
f->setAllTextures("mese.png");
f->setInventoryTextureCube("mese.png", "mese.png", "mese.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setStoneLikeDiggingProperties(f->digging_properties, 0.5);
i = CONTENT_CLOUD;
f = &content_features(i);
f->setAllTextures("cloud.png");
f->setInventoryTextureCube("cloud.png", "cloud.png", "cloud.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_AIR;
f = &content_features(i);
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0;
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->air_equivalent = true;
i = CONTENT_WATER;
f = &content_features(i);
f->setInventoryTextureCube("water.png", "water.png", "water.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->solidness = 0; // Drawn separately, makes no faces
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->liquid_type = LIQUID_FLOWING;
f->liquid_alternative_flowing = CONTENT_WATER;
i = CONTENT_WATERSOURCE;
f = &content_features(i);
//f->setInventoryTexture("water.png");
f->setInventoryTextureCube("water.png", "water.png", "water.png");
if(new_style_water)
{
f->solidness = 0; // drawn separately, makes no faces
}
else // old style
{
f->solidness = 1;
TileSpec t;
if(g_texturesource)
t.texture = g_texturesource->getTexture("water.png");
t.alpha = WATER_ALPHA;
t.material_type = MATERIAL_ALPHA_VERTEX;
t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
f->setAllTiles(t);
}
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->liquid_type = LIQUID_SOURCE;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->liquid_alternative_flowing = CONTENT_WATER;
i = CONTENT_TORCH;
f = &content_features(i);
f->setInventoryTexture("torch_on_floor.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
f->wall_mounted = true;
f->air_equivalent = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->light_source = LIGHT_MAX-1;
f->digging_properties.set("", DiggingProperties(true, 0.0, 0));
i = CONTENT_SIGN_WALL;
f = &content_features(i);
f->setInventoryTexture("sign_wall.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
f->wall_mounted = true;
f->air_equivalent = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
if(f->initial_metadata == NULL)
f->initial_metadata = new SignNodeMetadata("Some sign");
f->digging_properties.set("", DiggingProperties(true, 0.5, 0));
i = CONTENT_CHEST;
f = &content_features(i);
f->param_type = CPT_FACEDIR_SIMPLE;
f->setAllTextures("chest_side.png");
f->setTexture(0, "chest_top.png");
f->setTexture(1, "chest_top.png");
f->setTexture(5, "chest_front.png"); // Z-
f->setInventoryTexture("chest_top.png");
//f->setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png");
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
if(f->initial_metadata == NULL)
f->initial_metadata = new ChestNodeMetadata();
setWoodLikeDiggingProperties(f->digging_properties, 1.0);
i = CONTENT_FURNACE;
f = &content_features(i);
f->param_type = CPT_FACEDIR_SIMPLE;
f->setAllTextures("furnace_side.png");
f->setTexture(5, "furnace_front.png"); // Z-
f->setInventoryTexture("furnace_front.png");
//f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 6";
if(f->initial_metadata == NULL)
f->initial_metadata = new FurnaceNodeMetadata();
setStoneLikeDiggingProperties(f->digging_properties, 3.0);
i = CONTENT_COBBLE;
f = &content_features(i);
f->setAllTextures("cobble.png");
f->setInventoryTextureCube("cobble.png", "cobble.png", "cobble.png");
f->param_type = CPT_NONE;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setStoneLikeDiggingProperties(f->digging_properties, 0.9);
i = CONTENT_MOSSYCOBBLE;
f = &content_features(i);
f->setAllTextures("mossycobble.png");
f->setInventoryTextureCube("mossycobble.png", "mossycobble.png", "mossycobble.png");
f->param_type = CPT_NONE;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setStoneLikeDiggingProperties(f->digging_properties, 0.8);
i = CONTENT_STEEL;
f = &content_features(i);
f->setAllTextures("steel_block.png");
f->setInventoryTextureCube("steel_block.png", "steel_block.png",
"steel_block.png");
f->param_type = CPT_NONE;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
setStoneLikeDiggingProperties(f->digging_properties, 5.0);
// NOTE: Remember to add frequently used stuff to the texture atlas in tile.cpp
/*
Add MesePick to everything
*/
for(u16 i=0; i<256; i++)
{
content_features(i).digging_properties.set("MesePick",
DiggingProperties(true, 0.0, 65535./1337));
}
}
void setStoneLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
{
list.set("",
DiggingProperties(true, 15.0*toughness, 0));
list.set("WPick",
DiggingProperties(true, 1.3*toughness, 65535./30.*toughness));
list.set("STPick",
DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
list.set("SteelPick",
DiggingProperties(true, 0.50*toughness, 65535./333.*toughness));
/*list.set("MesePick",
DiggingProperties(true, 0.0*toughness, 65535./20.*toughness));*/
}
void setDirtLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
{
list.set("",
DiggingProperties(true, 0.75*toughness, 0));
list.set("WShovel",
DiggingProperties(true, 0.4*toughness, 65535./50.*toughness));
list.set("STShovel",
DiggingProperties(true, 0.2*toughness, 65535./150.*toughness));
list.set("SteelShovel",
DiggingProperties(true, 0.15*toughness, 65535./400.*toughness));
}
void setWoodLikeDiggingProperties(DiggingPropertiesList &list, float toughness)
{
list.set("",
DiggingProperties(true, 3.0*toughness, 0));
list.set("WAxe",
DiggingProperties(true, 1.5*toughness, 65535./30.*toughness));
list.set("STAxe",
DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
list.set("SteelAxe",
DiggingProperties(true, 0.5*toughness, 65535./333.*toughness));
}

62
src/content_mapnode.h Normal file

@ -0,0 +1,62 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 CONTENT_MAPNODE_HEADER
#define CONTENT_MAPNODE_HEADER
void content_mapnode_init();
/*
Node content type IDs
*/
#define CONTENT_STONE 0
#define CONTENT_GRASS 1
#define CONTENT_WATER 2
#define CONTENT_TORCH 3
#define CONTENT_TREE 4
#define CONTENT_LEAVES 5
#define CONTENT_GRASS_FOOTSTEPS 6
#define CONTENT_MESE 7
#define CONTENT_MUD 8
#define CONTENT_WATERSOURCE 9
// Pretty much useless, clouds won't be drawn this way
#define CONTENT_CLOUD 10
#define CONTENT_COALSTONE 11
#define CONTENT_WOOD 12
#define CONTENT_SAND 13
#define CONTENT_SIGN_WALL 14
#define CONTENT_CHEST 15
#define CONTENT_FURNACE 16
//#define CONTENT_WORKBENCH 17
#define CONTENT_COBBLE 18
#define CONTENT_STEEL 19
#define CONTENT_GLASS 20
#define CONTENT_FENCE 21
#define CONTENT_MOSSYCOBBLE 22
#define CONTENT_GRAVEL 23
#define CONTENT_SANDSTONE 24
#define CONTENT_CACTUS 25
#define CONTENT_BRICK 26
#define CONTENT_CLAY 27
#define CONTENT_PAPYRUS 28
#define CONTENT_BOOKSHELF 29
#define CONTENT_RAIL 30
#endif

321
src/content_nodemeta.cpp Normal file

@ -0,0 +1,321 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "content_nodemeta.h"
#include "inventory.h"
#include "content_mapnode.h"
/*
SignNodeMetadata
*/
// Prototype
SignNodeMetadata proto_SignNodeMetadata("");
SignNodeMetadata::SignNodeMetadata(std::string text):
m_text(text)
{
NodeMetadata::registerType(typeId(), create);
}
u16 SignNodeMetadata::typeId() const
{
return CONTENT_SIGN_WALL;
}
NodeMetadata* SignNodeMetadata::create(std::istream &is)
{
std::string text = deSerializeString(is);
return new SignNodeMetadata(text);
}
NodeMetadata* SignNodeMetadata::clone()
{
return new SignNodeMetadata(m_text);
}
void SignNodeMetadata::serializeBody(std::ostream &os)
{
os<<serializeString(m_text);
}
std::string SignNodeMetadata::infoText()
{
return std::string("\"")+m_text+"\"";
}
/*
ChestNodeMetadata
*/
// Prototype
ChestNodeMetadata proto_ChestNodeMetadata;
ChestNodeMetadata::ChestNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);
m_inventory = new Inventory();
m_inventory->addList("0", 8*4);
}
ChestNodeMetadata::~ChestNodeMetadata()
{
delete m_inventory;
}
u16 ChestNodeMetadata::typeId() const
{
return CONTENT_CHEST;
}
NodeMetadata* ChestNodeMetadata::create(std::istream &is)
{
ChestNodeMetadata *d = new ChestNodeMetadata();
d->m_inventory->deSerialize(is);
return d;
}
NodeMetadata* ChestNodeMetadata::clone()
{
ChestNodeMetadata *d = new ChestNodeMetadata();
*d->m_inventory = *m_inventory;
return d;
}
void ChestNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
}
std::string ChestNodeMetadata::infoText()
{
return "Chest";
}
bool ChestNodeMetadata::nodeRemovalDisabled()
{
/*
Disable removal if chest contains something
*/
InventoryList *list = m_inventory->getList("0");
if(list == NULL)
return false;
if(list->getUsedSlots() == 0)
return false;
return true;
}
std::string ChestNodeMetadata::getInventoryDrawSpecString()
{
return
"invsize[8,9;]"
"list[current_name;0;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]";
}
/*
FurnaceNodeMetadata
*/
// Prototype
FurnaceNodeMetadata proto_FurnaceNodeMetadata;
FurnaceNodeMetadata::FurnaceNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);
m_inventory = new Inventory();
m_inventory->addList("fuel", 1);
m_inventory->addList("src", 1);
m_inventory->addList("dst", 4);
m_step_accumulator = 0;
m_fuel_totaltime = 0;
m_fuel_time = 0;
m_src_totaltime = 0;
m_src_time = 0;
}
FurnaceNodeMetadata::~FurnaceNodeMetadata()
{
delete m_inventory;
}
u16 FurnaceNodeMetadata::typeId() const
{
return CONTENT_FURNACE;
}
NodeMetadata* FurnaceNodeMetadata::clone()
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
*d->m_inventory = *m_inventory;
return d;
}
NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
d->m_inventory->deSerialize(is);
int temp;
is>>temp;
d->m_fuel_totaltime = (float)temp/10;
is>>temp;
d->m_fuel_time = (float)temp/10;
return d;
}
void FurnaceNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
os<<itos(m_fuel_totaltime*10)<<" ";
os<<itos(m_fuel_time*10)<<" ";
}
std::string FurnaceNodeMetadata::infoText()
{
//return "Furnace";
if(m_fuel_time >= m_fuel_totaltime)
{
InventoryList *src_list = m_inventory->getList("src");
assert(src_list);
InventoryItem *src_item = src_list->getItem(0);
if(src_item)
return "Furnace is out of fuel";
else
return "Furnace is inactive";
}
else
{
std::string s = "Furnace is active (";
s += itos(m_fuel_time/m_fuel_totaltime*100);
s += "%)";
return s;
}
}
void FurnaceNodeMetadata::inventoryModified()
{
dstream<<"Furnace inventory modification callback"<<std::endl;
}
bool FurnaceNodeMetadata::step(float dtime)
{
if(dtime > 60.0)
dstream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
// Update at a fixed frequency
const float interval = 2.0;
m_step_accumulator += dtime;
bool changed = false;
while(m_step_accumulator > interval)
{
m_step_accumulator -= interval;
dtime = interval;
//dstream<<"Furnace step dtime="<<dtime<<std::endl;
InventoryList *dst_list = m_inventory->getList("dst");
assert(dst_list);
InventoryList *src_list = m_inventory->getList("src");
assert(src_list);
InventoryItem *src_item = src_list->getItem(0);
// Start only if there are free slots in dst, so that it can
// accomodate any result item
if(dst_list->getFreeSlots() > 0 && src_item && src_item->isCookable())
{
m_src_totaltime = 3;
}
else
{
m_src_time = 0;
m_src_totaltime = 0;
}
/*
If fuel is burning, increment the burn counters.
If item finishes cooking, move it to result.
*/
if(m_fuel_time < m_fuel_totaltime)
{
//dstream<<"Furnace is active"<<std::endl;
m_fuel_time += dtime;
m_src_time += dtime;
if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
&& src_item)
{
InventoryItem *cookresult = src_item->createCookResult();
dst_list->addItem(cookresult);
src_list->decrementMaterials(1);
m_src_time = 0;
m_src_totaltime = 0;
}
changed = true;
continue;
}
/*
If there is no source item or source item is not cookable, stop loop.
*/
if(src_item == NULL || m_src_totaltime < 0.001)
{
m_step_accumulator = 0;
break;
}
//dstream<<"Furnace is out of fuel"<<std::endl;
InventoryList *fuel_list = m_inventory->getList("fuel");
assert(fuel_list);
InventoryItem *fuel_item = fuel_list->getItem(0);
if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
{
m_fuel_totaltime = 30;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
{
m_fuel_totaltime = 30/4;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else if(ItemSpec(ITEM_CRAFT, "Stick").checkItem(fuel_item))
{
m_fuel_totaltime = 30/4/4;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
{
m_fuel_totaltime = 40;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else
{
//dstream<<"No fuel found"<<std::endl;
// No fuel, stop loop.
m_step_accumulator = 0;
break;
}
}
return changed;
}
std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
{
return
"invsize[8,9;]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"
"list[current_player;main;0,5;8,4;]";
}

92
src/content_nodemeta.h Normal file

@ -0,0 +1,92 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 CONTENT_NODEMETA_HEADER
#define CONTENT_NODEMETA_HEADER
#include "nodemetadata.h"
class Inventory;
class SignNodeMetadata : public NodeMetadata
{
public:
SignNodeMetadata(std::string text);
//~SignNodeMetadata();
virtual u16 typeId() const;
static NodeMetadata* create(std::istream &is);
virtual NodeMetadata* clone();
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
std::string getText(){ return m_text; }
void setText(std::string t){ m_text = t; }
private:
std::string m_text;
};
class ChestNodeMetadata : public NodeMetadata
{
public:
ChestNodeMetadata();
~ChestNodeMetadata();
virtual u16 typeId() const;
static NodeMetadata* create(std::istream &is);
virtual NodeMetadata* clone();
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual bool nodeRemovalDisabled();
virtual std::string getInventoryDrawSpecString();
private:
Inventory *m_inventory;
};
class FurnaceNodeMetadata : public NodeMetadata
{
public:
FurnaceNodeMetadata();
~FurnaceNodeMetadata();
virtual u16 typeId() const;
virtual NodeMetadata* clone();
static NodeMetadata* create(std::istream &is);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual void inventoryModified();
virtual bool step(float dtime);
virtual std::string getInventoryDrawSpecString();
private:
Inventory *m_inventory;
float m_step_accumulator;
float m_fuel_totaltime;
float m_fuel_time;
float m_src_totaltime;
float m_src_time;
};
#endif

@ -49,7 +49,7 @@ void set_default_settings()
g_settings.setDefault("wanted_fps", "30"); g_settings.setDefault("wanted_fps", "30");
g_settings.setDefault("fps_max", "60"); g_settings.setDefault("fps_max", "60");
g_settings.setDefault("viewing_range_nodes_max", "300"); g_settings.setDefault("viewing_range_nodes_max", "300");
g_settings.setDefault("viewing_range_nodes_min", "35"); g_settings.setDefault("viewing_range_nodes_min", "25");
g_settings.setDefault("screenW", "800"); g_settings.setDefault("screenW", "800");
g_settings.setDefault("screenH", "600"); g_settings.setDefault("screenH", "600");
g_settings.setDefault("address", ""); g_settings.setDefault("address", "");
@ -63,22 +63,28 @@ void set_default_settings()
g_settings.setDefault("enable_texture_atlas", "true"); g_settings.setDefault("enable_texture_atlas", "true");
g_settings.setDefault("texture_path", ""); g_settings.setDefault("texture_path", "");
g_settings.setDefault("video_driver", "opengl"); g_settings.setDefault("video_driver", "opengl");
g_settings.setDefault("free_move", "false"); g_settings.setDefault("free_move", "false");
g_settings.setDefault("continuous_forward", "false"); g_settings.setDefault("continuous_forward", "false");
g_settings.setDefault("fast_move", "false"); g_settings.setDefault("fast_move", "false");
g_settings.setDefault("invert_mouse", "false");
g_settings.setDefault("enable_farmesh", "false");
g_settings.setDefault("enable_clouds", "true");
g_settings.setDefault("invisible_stone", "false");
// Server stuff // Server stuff
g_settings.setDefault("enable_experimental", "false"); g_settings.setDefault("enable_experimental", "false");
g_settings.setDefault("creative_mode", "false"); g_settings.setDefault("creative_mode", "false");
g_settings.setDefault("enable_damage", "false"); //TODO: Set to true g_settings.setDefault("enable_damage", "false"); //TODO: Set to true when healing is possible
g_settings.setDefault("give_initial_stuff", "false"); g_settings.setDefault("give_initial_stuff", "false");
g_settings.setDefault("default_password", "");
g_settings.setDefault("default_privs", "build, shout");
g_settings.setDefault("profiler_print_interval", "0");
g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");
g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); //g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
//g_settings.setDefault("max_simultaneous_block_sends_per_client", "2"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "8");
g_settings.setDefault("max_block_send_distance", "8"); g_settings.setDefault("max_block_send_distance", "8");
g_settings.setDefault("max_block_generate_distance", "8"); g_settings.setDefault("max_block_generate_distance", "8");
g_settings.setDefault("time_send_interval", "20"); g_settings.setDefault("time_send_interval", "20");
@ -86,5 +92,6 @@ void set_default_settings()
g_settings.setDefault("server_unload_unused_sectors_timeout", "60"); g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
g_settings.setDefault("server_map_save_interval", "60"); g_settings.setDefault("server_map_save_interval", "60");
g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0"); g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0");
//g_settings.setDefault("dungeon_rarity", "0.025");
} }

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h" #include "filesys.h"
#include "porting.h" #include "porting.h"
#include "collision.h" #include "collision.h"
#include "content_mapnode.h"
Environment::Environment(): Environment::Environment():
@ -426,7 +427,14 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
testplayer.deSerialize(is); testplayer.deSerialize(is);
} }
dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl; if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
{
dstream<<"Not loading player with invalid name: "
<<testplayer.getName()<<std::endl;
}
dstream<<"Loaded test player with name "<<testplayer.getName()
<<std::endl;
// Search for the player // Search for the player
std::string playername = testplayer.getName(); std::string playername = testplayer.getName();
@ -571,6 +579,66 @@ void spawnRandomObjects(MapBlock *block)
} }
#endif #endif
void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
{
// Get time difference
u32 dtime_s = 0;
u32 stamp = block->getTimestamp();
if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
dtime_s = m_game_time - block->getTimestamp();
dtime_s += additional_dtime;
// Set current time as timestamp (and let it set ChangedFlag)
block->setTimestamp(m_game_time);
//dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
// Activate stored objects
activateObjects(block);
// Run node metadata
bool changed = block->m_node_metadata.step((float)dtime_s);
if(changed)
{
MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
event.p = block->getPos();
m_map->dispatchEvent(&event);
block->setChangedFlag();
}
// TODO: Do something
// TODO: Implement usage of ActiveBlockModifier
// Here's a quick demonstration
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0);
#if 1
// Test something:
// Convert all mud under proper day lighting to grass
if(n.d == CONTENT_MUD)
{
if(dtime_s > 300)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(content_features(n_top.d).air_equivalent &&
n_top.getLight(LIGHTBANK_DAY) >= 13)
{
n.d = CONTENT_GRASS;
m_map->addNodeWithEvent(p, n);
}
}
}
#endif
}
}
void ServerEnvironment::step(float dtime) void ServerEnvironment::step(float dtime)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
@ -686,8 +754,8 @@ void ServerEnvironment::step(float dtime)
MapBlock *block = m_map->getBlockNoCreateNoEx(p); MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL) if(block==NULL)
continue; continue;
// Set current time as timestamp (and let it set ChangedFlag)
// Set current time as timestamp
block->setTimestamp(m_game_time); block->setTimestamp(m_game_time);
} }
@ -714,7 +782,7 @@ void ServerEnvironment::step(float dtime)
if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
dtime_s = m_game_time - block->getTimestamp(); dtime_s = m_game_time - block->getTimestamp();
// Set current time as timestamp // Set current time as timestamp (and let it set ChangedFlag)
block->setTimestamp(m_game_time); block->setTimestamp(m_game_time);
//dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl; //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
@ -722,7 +790,20 @@ void ServerEnvironment::step(float dtime)
// Activate stored objects // Activate stored objects
activateObjects(block); activateObjects(block);
// Run node metadata
bool changed = block->m_node_metadata.step((float)dtime_s);
if(changed)
{
MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
event.p = p;
m_map->dispatchEvent(&event);
block->setChangedFlag();
}
// TODO: Do something // TODO: Do something
// TODO: Implement usage of ActiveBlockModifier
// Here's a quick demonstration // Here's a quick demonstration
v3s16 p0; v3s16 p0;
@ -736,7 +817,7 @@ void ServerEnvironment::step(float dtime)
// Convert all mud under proper day lighting to grass // Convert all mud under proper day lighting to grass
if(n.d == CONTENT_MUD) if(n.d == CONTENT_MUD)
{ {
if(1) if(dtime_s > 300)
{ {
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(content_features(n_top.d).air_equivalent && if(content_features(n_top.d).air_equivalent &&
@ -747,6 +828,22 @@ void ServerEnvironment::step(float dtime)
} }
} }
} }
/*
Convert grass into mud if under something else than air
*/
else if(n.d == CONTENT_GRASS)
{
//if(myrand()%20 == 0)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(n_top.d != CONTENT_AIR
&& n_top.d != CONTENT_IGNORE)
{
n.d = CONTENT_MUD;
m_map->addNodeWithEvent(p, n);
}
}
}
} }
} }
} }
@ -754,8 +851,10 @@ void ServerEnvironment::step(float dtime)
/* /*
Mess around in active blocks Mess around in active blocks
*/ */
if(m_active_blocks_test_interval.step(dtime, 5.0)) if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
{ {
float dtime = 1.0;
for(core::map<v3s16, bool>::Iterator for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.m_list.getIterator(); i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++) i.atEnd()==false; i++)
@ -770,7 +869,40 @@ void ServerEnvironment::step(float dtime)
continue; continue;
// Set current time as timestamp // Set current time as timestamp
block->setTimestamp(m_game_time); block->setTimestampNoChangedFlag(m_game_time);
// Run node metadata
bool changed = block->m_node_metadata.step(dtime);
if(changed)
{
MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
event.p = p;
m_map->dispatchEvent(&event);
block->setChangedFlag();
}
}
}
if(m_active_blocks_test_interval.step(dtime, 10.0))
{
//float dtime = 10.0;
for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++)
{
v3s16 p = i.getNode()->getKey();
/*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") being handled"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
continue;
// Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
/* /*
Do stuff! Do stuff!
@ -784,6 +916,7 @@ void ServerEnvironment::step(float dtime)
Everything should bind to inside this single content Everything should bind to inside this single content
searching loop to keep things fast. searching loop to keep things fast.
*/ */
// TODO: Implement usage of ActiveBlockModifier
v3s16 p0; v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
@ -792,11 +925,14 @@ void ServerEnvironment::step(float dtime)
{ {
v3s16 p = p0 + block->getPosRelative(); v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0); MapNode n = block->getNodeNoEx(p0);
// Test something:
// Convert mud under proper lighting to grass /*
Test something:
Convert mud under proper lighting to grass
*/
if(n.d == CONTENT_MUD) if(n.d == CONTENT_MUD)
{ {
if(myrand()%10 == 0) if(myrand()%20 == 0)
{ {
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(content_features(n_top.d).air_equivalent && if(content_features(n_top.d).air_equivalent &&
@ -807,6 +943,22 @@ void ServerEnvironment::step(float dtime)
} }
} }
} }
/*
Convert grass into mud if under something else than air
*/
else if(n.d == CONTENT_GRASS)
{
//if(myrand()%20 == 0)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(n_top.d != CONTENT_AIR
&& n_top.d != CONTENT_IGNORE)
{
n.d = CONTENT_MUD;
m_map->addNodeWithEvent(p, n);
}
}
}
} }
} }
} }
@ -941,49 +1093,8 @@ u16 getFreeServerActiveObjectId(
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{ {
assert(object); assert(object);
if(object->getId() == 0) u16 id = addActiveObjectRaw(object, true);
{ return id;
u16 new_id = getFreeServerActiveObjectId(m_active_objects);
if(new_id == 0)
{
dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
<<"no free ids available"<<std::endl;
delete object;
return 0;
}
object->setId(new_id);
}
if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
{
dstream<<"WARNING: ServerEnvironment::addActiveObject(): "
<<"id is not free ("<<object->getId()<<")"<<std::endl;
delete object;
return 0;
}
/*dstream<<"INGO: ServerEnvironment::addActiveObject(): "
<<"added (id="<<object->getId()<<")"<<std::endl;*/
m_active_objects.insert(object->getId(), object);
// Add static object to active static list of the block
v3f objectpos = object->getBasePosition();
std::string staticdata = object->getStaticData();
StaticObject s_obj(object->getType(), objectpos, staticdata);
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
if(block)
{
block->m_static_objects.m_active.insert(object->getId(), s_obj);
object->m_static_exists = true;
object->m_static_block = blockpos;
}
else{
dstream<<"WARNING: Server: Could not find a block for "
<<"storing newly added static active object"<<std::endl;
}
return object->getId();
} }
/* /*
@ -1086,6 +1197,58 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
************ Private methods ************* ************ Private methods *************
*/ */
u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
bool set_changed)
{
assert(object);
if(object->getId() == 0)
{
u16 new_id = getFreeServerActiveObjectId(m_active_objects);
if(new_id == 0)
{
dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
<<"no free ids available"<<std::endl;
delete object;
return 0;
}
object->setId(new_id);
}
if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
{
dstream<<"WARNING: ServerEnvironment::addActiveObjectRaw(): "
<<"id is not free ("<<object->getId()<<")"<<std::endl;
delete object;
return 0;
}
/*dstream<<"INGO: ServerEnvironment::addActiveObjectRaw(): "
<<"added (id="<<object->getId()<<")"<<std::endl;*/
m_active_objects.insert(object->getId(), object);
// Add static object to active static list of the block
v3f objectpos = object->getBasePosition();
std::string staticdata = object->getStaticData();
StaticObject s_obj(object->getType(), objectpos, staticdata);
// Add to the block where the object is located in
v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
if(block)
{
block->m_static_objects.m_active.insert(object->getId(), s_obj);
object->m_static_exists = true;
object->m_static_block = blockpos;
if(set_changed)
block->setChangedFlag();
}
else{
dstream<<"WARNING: Server: Could not find a block for "
<<"storing newly added static active object"<<std::endl;
}
return object->getId();
}
/* /*
Remove objects that satisfy (m_removed && m_known_by_count==0) Remove objects that satisfy (m_removed && m_known_by_count==0)
*/ */
@ -1176,8 +1339,8 @@ void ServerEnvironment::activateObjects(MapBlock *block)
continue; continue;
} }
// This will also add the object to the active static list // This will also add the object to the active static list
addActiveObject(obj); addActiveObjectRaw(obj, false);
//u16 id = addActiveObject(obj); //u16 id = addActiveObjectRaw(obj, false);
} }
// Clear stored list // Clear stored list
block->m_static_objects.m_stored.clear(); block->m_static_objects.m_stored.clear();
@ -1190,7 +1353,8 @@ void ServerEnvironment::activateObjects(MapBlock *block)
block->m_static_objects.m_stored.push_back(s_obj); block->m_static_objects.m_stored.push_back(s_obj);
} }
// Block has been modified // Block has been modified
block->setChangedFlag(); // NOTE: No it has not really. Save I/O here.
//block->setChangedFlag();
} }
/* /*

@ -112,25 +112,6 @@ public:
private: private:
}; };
/*
Active block modifier interface
*/
class ServerEnvironment;
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
virtual u32 getTriggerContentCount(){ return 1;}
virtual u8 getTriggerContent(u32 i) = 0;
virtual float getActiveInterval() = 0;
virtual u32 getActiveChance() = 0;
virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
};
/* /*
The server-side environment. The server-side environment.
@ -140,6 +121,7 @@ public:
#include "serverobject.h" #include "serverobject.h"
class Server; class Server;
class ActiveBlockModifier;
class ServerEnvironment : public Environment class ServerEnvironment : public Environment
{ {
@ -177,7 +159,8 @@ public:
void loadMeta(const std::string &savedir); void loadMeta(const std::string &savedir);
/* /*
ActiveObjects External ActiveObject interface
-------------------------------------------
*/ */
ServerActiveObject* getActiveObject(u16 id); ServerActiveObject* getActiveObject(u16 id);
@ -214,7 +197,38 @@ public:
*/ */
ActiveObjectMessage getActiveObjectMessage(); ActiveObjectMessage getActiveObjectMessage();
/*
Activate objects and dynamically modify for the dtime determined
from timestamp and additional_dtime
*/
void activateBlock(MapBlock *block, u32 additional_dtime=0);
/*
ActiveBlockModifiers (TODO)
-------------------------------------------
*/
void addActiveBlockModifier(ActiveBlockModifier *abm);
private: private:
/*
Internal ActiveObject interface
-------------------------------------------
*/
/*
Add an active object to the environment.
Called by addActiveObject.
Object may be deleted by environment immediately.
If id of object is 0, assigns a free id to it.
Returns the id of the object.
Returns 0 if not added and thus deleted.
*/
u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed);
/* /*
Remove all objects that satisfy (m_removed && m_known_by_count==0) Remove all objects that satisfy (m_removed && m_known_by_count==0)
*/ */
@ -256,6 +270,7 @@ private:
ActiveBlockList m_active_blocks; ActiveBlockList m_active_blocks;
IntervalLimiter m_active_blocks_management_interval; IntervalLimiter m_active_blocks_management_interval;
IntervalLimiter m_active_blocks_test_interval; IntervalLimiter m_active_blocks_test_interval;
IntervalLimiter m_active_blocks_nodemetadata_interval;
// Time from the beginning of the game in seconds. // Time from the beginning of the game in seconds.
// Incremented in step(). // Incremented in step().
u32 m_game_time; u32 m_game_time;
@ -263,6 +278,29 @@ private:
float m_game_time_fraction_counter; float m_game_time_fraction_counter;
}; };
/*
Active block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
*/
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
//virtual core::list<u8> update(ServerEnvironment *env) = 0;
virtual u32 getTriggerContentCount(){ return 1;}
virtual u8 getTriggerContent(u32 i) = 0;
virtual float getActiveInterval() = 0;
// chance of (1 / return value), 0 is disallowed
virtual u32 getActiveChance() = 0;
// This is called usually at interval for 1/chance of the nodes
virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
};
#ifndef SERVER #ifndef SERVER
#include "clientobject.h" #include "clientobject.h"

413
src/farmesh.cpp Normal file

@ -0,0 +1,413 @@
/*
Part of Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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.
*/
/*
A quick messy implementation of terrain rendering for a long
distance according to map seed
*/
#include "farmesh.h"
#include "constants.h"
#include "debug.h"
#include "noise.h"
#include "map.h"
#include "client.h"
#include "mapgen.h"
FarMesh::FarMesh(
scene::ISceneNode* parent,
scene::ISceneManager* mgr,
s32 id,
u64 seed,
Client *client
):
scene::ISceneNode(parent, mgr, id),
m_seed(seed),
m_camera_pos(0,0),
m_time(0),
m_client(client),
m_render_range(20*MAP_BLOCKSIZE)
{
dstream<<__FUNCTION_NAME<<std::endl;
video::IVideoDriver* driver = mgr->getVideoDriver();
m_materials[0].setFlag(video::EMF_LIGHTING, false);
m_materials[0].setFlag(video::EMF_BACK_FACE_CULLING, true);
//m_materials[0].setFlag(video::EMF_BACK_FACE_CULLING, false);
m_materials[0].setFlag(video::EMF_BILINEAR_FILTER, false);
m_materials[0].setFlag(video::EMF_FOG_ENABLE, false);
//m_materials[0].setFlag(video::EMF_ANTI_ALIASING, true);
//m_materials[0].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
m_materials[0].setFlag(video::EMF_FOG_ENABLE, true);
m_materials[1].setFlag(video::EMF_LIGHTING, false);
m_materials[1].setFlag(video::EMF_BACK_FACE_CULLING, false);
m_materials[1].setFlag(video::EMF_BILINEAR_FILTER, false);
m_materials[1].setFlag(video::EMF_FOG_ENABLE, false);
m_materials[1].setTexture
(0, driver->getTexture(getTexturePath("treeprop.png").c_str()));
m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
m_materials[1].setFlag(video::EMF_FOG_ENABLE, true);
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*31000,-BS*1000000,
BS*1000000,BS*31000,BS*1000000);
}
FarMesh::~FarMesh()
{
dstream<<__FUNCTION_NAME<<std::endl;
}
u32 FarMesh::getMaterialCount() const
{
return FARMESH_MATERIAL_COUNT;
}
video::SMaterial& FarMesh::getMaterial(u32 i)
{
return m_materials[i];
}
void FarMesh::OnRegisterSceneNode()
{
if(IsVisible)
{
//SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
//SceneManager->registerNodeForRendering(this, scene::ESNRP_SKY_BOX);
}
ISceneNode::OnRegisterSceneNode();
}
#define MYROUND(x) (x > 0.0 ? (int)x : (int)x - 1)
// Temporary hack
struct HeightPoint
{
float gh; // ground height
float ma; // mud amount
float have_sand;
float tree_amount;
};
core::map<v2s16, HeightPoint> g_heights;
HeightPoint ground_height(u64 seed, v2s16 p2d)
{
core::map<v2s16, HeightPoint>::Node *n = g_heights.find(p2d);
if(n)
return n->getValue();
HeightPoint hp;
s16 level = mapgen::find_ground_level_from_noise(seed, p2d, 3);
hp.gh = (level-4)*BS;
hp.ma = (4)*BS;
/*hp.gh = BS*base_rock_level_2d(seed, p2d);
hp.ma = BS*get_mud_add_amount(seed, p2d);*/
hp.have_sand = mapgen::get_have_sand(seed, p2d);
if(hp.gh > BS*WATER_LEVEL)
hp.tree_amount = mapgen::tree_amount_2d(seed, p2d);
else
hp.tree_amount = 0;
// No mud has been added if mud amount is less than 1
if(hp.ma < 1.0*BS)
hp.ma = 0.0;
//hp.gh -= BS*3; // Lower a bit so that it is not that much in the way
g_heights[p2d] = hp;
return hp;
}
void FarMesh::render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();
/*if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT)
return;*/
if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SOLID)
return;
/*if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SKY_BOX)
return;*/
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
//const s16 grid_radius_i = 12;
//const float grid_size = BS*50;
const s16 grid_radius_i = m_render_range/MAP_BLOCKSIZE;
const float grid_size = BS*MAP_BLOCKSIZE;
const v2f grid_speed(-BS*0, 0);
// Position of grid noise origin in world coordinates
v2f world_grid_origin_pos_f(0,0);
// Position of grid noise origin from the camera
v2f grid_origin_from_camera_f = world_grid_origin_pos_f - m_camera_pos;
// The center point of drawing in the noise
v2f center_of_drawing_in_noise_f = -grid_origin_from_camera_f;
// The integer center point of drawing in the noise
v2s16 center_of_drawing_in_noise_i(
MYROUND(center_of_drawing_in_noise_f.X / grid_size),
MYROUND(center_of_drawing_in_noise_f.Y / grid_size)
);
// The world position of the integer center point of drawing in the noise
v2f world_center_of_drawing_in_noise_f = v2f(
center_of_drawing_in_noise_i.X * grid_size,
center_of_drawing_in_noise_i.Y * grid_size
) + world_grid_origin_pos_f;
for(s16 zi=-grid_radius_i; zi<grid_radius_i; zi++)
for(s16 xi=-grid_radius_i; xi<grid_radius_i; xi++)
{
/*// Don't draw very close to player
s16 dd = 3;
if(zi > -dd && zi < dd && xi > -dd && xi < dd)
continue;*/
v2s16 p_in_noise_i(
xi+center_of_drawing_in_noise_i.X,
zi+center_of_drawing_in_noise_i.Y
);
// If sector was drawn, don't draw it this way
if(m_client->m_env.getClientMap().sectorWasDrawn(p_in_noise_i))
continue;
/*if((p_in_noise_i.X + p_in_noise_i.Y)%2==0)
continue;*/
/*if((p_in_noise_i.X/2 + p_in_noise_i.Y/2)%2==0)
continue;*/
v2f p0 = v2f(xi,zi)*grid_size + world_center_of_drawing_in_noise_f;
/*double noise[4];
double d = 100*BS;
noise[0] = d*noise2d_perlin(
(float)(p_in_noise_i.X+0)*grid_size/BS/100,
(float)(p_in_noise_i.Y+0)*grid_size/BS/100,
m_seed, 3, 0.5);
noise[1] = d*noise2d_perlin(
(float)(p_in_noise_i.X+0)*grid_size/BS/100,
(float)(p_in_noise_i.Y+1)*grid_size/BS/100,
m_seed, 3, 0.5);
noise[2] = d*noise2d_perlin(
(float)(p_in_noise_i.X+1)*grid_size/BS/100,
(float)(p_in_noise_i.Y+1)*grid_size/BS/100,
m_seed, 3, 0.5);
noise[3] = d*noise2d_perlin(
(float)(p_in_noise_i.X+1)*grid_size/BS/100,
(float)(p_in_noise_i.Y+0)*grid_size/BS/100,
m_seed, 3, 0.5);*/
HeightPoint hps[5];
hps[0] = ground_height(m_seed, v2s16(
(p_in_noise_i.X+0)*grid_size/BS,
(p_in_noise_i.Y+0)*grid_size/BS));
hps[1] = ground_height(m_seed, v2s16(
(p_in_noise_i.X+0)*grid_size/BS,
(p_in_noise_i.Y+1)*grid_size/BS));
hps[2] = ground_height(m_seed, v2s16(
(p_in_noise_i.X+1)*grid_size/BS,
(p_in_noise_i.Y+1)*grid_size/BS));
hps[3] = ground_height(m_seed, v2s16(
(p_in_noise_i.X+1)*grid_size/BS,
(p_in_noise_i.Y+0)*grid_size/BS));
v2s16 centerpoint(
(p_in_noise_i.X+0)*grid_size/BS+MAP_BLOCKSIZE/2,
(p_in_noise_i.Y+0)*grid_size/BS+MAP_BLOCKSIZE/2);
hps[4] = ground_height(m_seed, centerpoint);
float noise[5];
float h_min = BS*65535;
float h_max = -BS*65536;
float ma_avg = 0;
float h_avg = 0;
u32 have_sand_count = 0;
float tree_amount_avg = 0;
for(u32 i=0; i<5; i++)
{
noise[i] = hps[i].gh + hps[i].ma;
if(noise[i] < h_min)
h_min = noise[i];
if(noise[i] > h_max)
h_max = noise[i];
ma_avg += hps[i].ma;
h_avg += noise[i];
if(hps[i].have_sand)
have_sand_count++;
tree_amount_avg += hps[i].tree_amount;
}
ma_avg /= 5.0;
h_avg /= 5.0;
tree_amount_avg /= 5.0;
float steepness = (h_max - h_min)/grid_size;
float light_f = noise[0]+noise[1]-noise[2]-noise[3];
light_f /= 100;
if(light_f < -1.0) light_f = -1.0;
if(light_f > 1.0) light_f = 1.0;
//light_f += 1.0;
//light_f /= 2.0;
v2f p1 = p0 + v2f(1,1)*grid_size;
bool ground_is_sand = false;
bool ground_is_rock = false;
bool ground_is_mud = false;
video::SColor c;
// Detect water
if(h_avg < WATER_LEVEL*BS && h_max < (WATER_LEVEL+5)*BS)
{
//c = video::SColor(255,59,86,146);
c = video::SColor(255,82,120,204);
/*// Set to water level
for(u32 i=0; i<4; i++)
{
if(noise[i] < BS*WATER_LEVEL)
noise[i] = BS*WATER_LEVEL;
}*/
light_f = 0;
}
// Steep cliffs
else if(steepness > 2.0)
{
c = video::SColor(255,128,128,128);
ground_is_rock = true;
}
// Basic ground
else
{
if(ma_avg < 2.0*BS)
{
c = video::SColor(255,128,128,128);
ground_is_rock = true;
}
else
{
if(h_avg <= 2.5*BS && have_sand_count >= 2)
{
c = video::SColor(255,210,194,156);
ground_is_sand = true;
}
else
{
/*// Trees if there are over 0.01 trees per MapNode
if(tree_amount_avg > 0.01)
c = video::SColor(255,50,128,50);
else
c = video::SColor(255,107,134,51);*/
c = video::SColor(255,107,134,51);
ground_is_mud = true;
}
}
}
// Set to water level
for(u32 i=0; i<4; i++)
{
if(noise[i] < BS*WATER_LEVEL)
noise[i] = BS*WATER_LEVEL;
}
float b = m_brightness + light_f*0.1*m_brightness;
if(b < 0) b = 0;
if(b > 2) b = 2;
c = video::SColor(255, b*c.getRed(), b*c.getGreen(), b*c.getBlue());
driver->setMaterial(m_materials[0]);
video::S3DVertex vertices[4] =
{
video::S3DVertex(p0.X,noise[0],p0.Y, 0,0,0, c, 0,1),
video::S3DVertex(p0.X,noise[1],p1.Y, 0,0,0, c, 1,1),
video::S3DVertex(p1.X,noise[2],p1.Y, 0,0,0, c, 1,0),
video::S3DVertex(p1.X,noise[3],p0.Y, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
// Add some trees if appropriate
if(tree_amount_avg >= 0.0065 && steepness < 1.4
&& ground_is_mud == true)
{
driver->setMaterial(m_materials[1]);
float b = m_brightness;
c = video::SColor(255, b*255, b*255, b*255);
{
video::S3DVertex vertices[4] =
{
video::S3DVertex(p0.X,noise[0],p0.Y,
0,0,0, c, 0,1),
video::S3DVertex(p0.X,noise[0]+BS*MAP_BLOCKSIZE,p0.Y,
0,0,0, c, 0,0),
video::S3DVertex(p1.X,noise[2]+BS*MAP_BLOCKSIZE,p1.Y,
0,0,0, c, 1,0),
video::S3DVertex(p1.X,noise[2],p1.Y,
0,0,0, c, 1,1),
};
u16 indices[] = {0,1,2,2,3,0};
driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
video::EVT_STANDARD, scene::EPT_TRIANGLES,
video::EIT_16BIT);
}
{
video::S3DVertex vertices[4] =
{
video::S3DVertex(p1.X,noise[3],p0.Y,
0,0,0, c, 0,1),
video::S3DVertex(p1.X,noise[3]+BS*MAP_BLOCKSIZE,p0.Y,
0,0,0, c, 0,0),
video::S3DVertex(p0.X,noise[1]+BS*MAP_BLOCKSIZE,p1.Y,
0,0,0, c, 1,0),
video::S3DVertex(p0.X,noise[1],p1.Y,
0,0,0, c, 1,1),
};
u16 indices[] = {0,1,2,2,3,0};
driver->drawVertexPrimitiveList(vertices, 4, indices, 2,
video::EVT_STANDARD, scene::EPT_TRIANGLES,
video::EIT_16BIT);
}
}
}
//driver->clearZBuffer();
}
void FarMesh::step(float dtime)
{
m_time += dtime;
}
void FarMesh::update(v2f camera_p, float brightness, s16 render_range)
{
m_camera_pos = camera_p;
m_brightness = brightness;
m_render_range = render_range;
}

85
src/farmesh.h Normal file

@ -0,0 +1,85 @@
/*
Part of Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 FARMESH_HEADER
#define FARMESH_HEADER
/*
A quick messy implementation of terrain rendering for a long
distance according to map seed
*/
#include "common_irrlicht.h"
#define FARMESH_MATERIAL_COUNT 2
class Client;
class FarMesh : public scene::ISceneNode
{
public:
FarMesh(
scene::ISceneNode* parent,
scene::ISceneManager* mgr,
s32 id,
u64 seed,
Client *client
);
~FarMesh();
/*
ISceneNode methods
*/
virtual void OnRegisterSceneNode();
virtual void render();
virtual const core::aabbox3d<f32>& getBoundingBox() const
{
return m_box;
}
virtual u32 getMaterialCount() const;
virtual video::SMaterial& getMaterial(u32 i);
/*
Other stuff
*/
void step(float dtime);
void update(v2f camera_p, float brightness, s16 render_range);
private:
video::SMaterial m_materials[FARMESH_MATERIAL_COUNT];
core::aabbox3d<f32> m_box;
float m_cloud_y;
float m_brightness;
u64 m_seed;
v2f m_camera_pos;
float m_time;
Client *m_client;
s16 m_render_range;
};
#endif

@ -25,11 +25,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiPasswordChange.h" #include "guiPasswordChange.h"
#include "guiInventoryMenu.h" #include "guiInventoryMenu.h"
#include "guiTextInputMenu.h" #include "guiTextInputMenu.h"
#include "guiFurnaceMenu.h"
#include "materials.h" #include "materials.h"
#include "config.h" #include "config.h"
#include "clouds.h" #include "clouds.h"
#include "keycode.h" #include "keycode.h"
#include "farmesh.h"
// TODO: Move content-aware stuff to separate file
#include "content_mapnode.h"
#include "content_nodemeta.h"
/* /*
Setting this to 1 enables a special camera mode that forces Setting this to 1 enables a special camera mode that forces
@ -358,7 +362,7 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
core::rect<s32>(core::position2d<s32>(0,0), core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(heart_texture->getOriginalSize())), core::dimension2di(heart_texture->getOriginalSize())),
NULL, colors, true); NULL, colors, true);
p += v2s32(20,0); p += v2s32(16,0);
} }
if(halfheartcount % 2 == 1) if(halfheartcount % 2 == 1)
{ {
@ -371,7 +375,7 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
driver->draw2DImage(heart_texture, rect, driver->draw2DImage(heart_texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), srcd), core::rect<s32>(core::position2d<s32>(0,0), srcd),
NULL, colors, true); NULL, colors, true);
p += v2s32(20,0); p += v2s32(16,0);
} }
} }
} }
@ -632,6 +636,10 @@ void update_skybox(video::IVideoDriver* driver,
skybox->remove(); skybox->remove();
} }
/*// Disable skybox if FarMesh is enabled
if(g_settings.getBool("enable_farmesh"))
return;*/
if(brightness >= 0.5) if(brightness >= 0.5)
{ {
skybox = smgr->addSkyBoxSceneNode( skybox = smgr->addSkyBoxSceneNode(
@ -801,8 +809,9 @@ void the_game(
{ {
if(client.accessDenied()) if(client.accessDenied())
{ {
error_message = L"Access denied. Check your password and try again."; error_message = L"Access denied. Reason: "
std::cout<<DTIME<<"Access denied."<<std::endl; +client.accessDeniedReason();
std::cout<<DTIME<<wide_to_narrow(error_message)<<std::endl;
} }
else else
{ {
@ -851,8 +860,21 @@ void the_game(
float cloud_height = BS*100; float cloud_height = BS*100;
Clouds *clouds = NULL; Clouds *clouds = NULL;
if(g_settings.getBool("enable_clouds"))
{
clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1,
cloud_height, time(0)); cloud_height, time(0));
}
/*
FarMesh
*/
FarMesh *farmesh = NULL;
if(g_settings.getBool("enable_farmesh"))
{
farmesh = new FarMesh(smgr->getRootSceneNode(), smgr, -1, client.getMapSeed(), &client);
}
/* /*
Move into game Move into game
@ -886,8 +908,8 @@ void the_game(
gui::IGUIStaticText *guitext_chat = guienv->addStaticText( gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
L"", L"",
core::rect<s32>(0,0,0,0), core::rect<s32>(0,0,0,0),
false, false); // Disable word wrap as of now //false, false); // Disable word wrap as of now
//false, true); false, true);
//guitext_chat->setBackgroundColor(video::SColor(96,0,0,0)); //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
core::list<ChatLine> chat_lines; core::list<ChatLine> chat_lines;
@ -930,6 +952,9 @@ void the_game(
core::list<float> frametime_log; core::list<float> frametime_log;
float damage_flash_timer = 0; float damage_flash_timer = 0;
s16 farmesh_range = 20*MAP_BLOCKSIZE;
bool invert_mouse = g_settings.getBool("invert_mouse");
/* /*
Main loop Main loop
@ -937,6 +962,10 @@ void the_game(
bool first_loop_after_window_activation = true; bool first_loop_after_window_activation = true;
// TODO: Convert the static interval timers to these
// Interval limiter for profiler
IntervalLimiter m_profiler_interval;
// Time is in milliseconds // Time is in milliseconds
// NOTE: getRealTime() causes strange problems in wine (imprecision?) // NOTE: getRealTime() causes strange problems in wine (imprecision?)
// 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
@ -1123,6 +1152,21 @@ void the_game(
} }
} }
/*
Profiler
*/
float profiler_print_interval =
g_settings.getFloat("profiler_print_interval");
if(profiler_print_interval != 0)
{
if(m_profiler_interval.step(0.030, profiler_print_interval))
{
dstream<<"Profiler:"<<std::endl;
g_profiler.print(dstream);
g_profiler.clear();
}
}
/* /*
Direct handling of user input Direct handling of user input
*/ */
@ -1189,10 +1233,12 @@ void the_game(
if(g_settings.getBool("free_move")) if(g_settings.getBool("free_move"))
{ {
g_settings.set("free_move","false"); g_settings.set("free_move","false");
chat_lines.push_back(ChatLine(L"free_move disabled"));
} }
else else
{ {
g_settings.set("free_move","true"); g_settings.set("free_move","true");
chat_lines.push_back(ChatLine(L"free_move enabled"));
} }
} }
else if(input->wasKeyDown(getKeySetting("keymap_fastmove"))) else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
@ -1200,10 +1246,12 @@ void the_game(
if(g_settings.getBool("fast_move")) if(g_settings.getBool("fast_move"))
{ {
g_settings.set("fast_move","false"); g_settings.set("fast_move","false");
chat_lines.push_back(ChatLine(L"fast_move disabled"));
} }
else else
{ {
g_settings.set("fast_move","true"); g_settings.set("fast_move","true");
chat_lines.push_back(ChatLine(L"fast_move enabled"));
} }
} }
@ -1355,7 +1403,11 @@ void the_game(
if((device->isWindowActive() && noMenuActive()) || random_input) if((device->isWindowActive() && noMenuActive()) || random_input)
{ {
if(!random_input) if(!random_input)
{
// Mac OSX gets upset if this is set every frame
if(device->getCursorControl()->isVisible())
device->getCursorControl()->setVisible(false); device->getCursorControl()->setVisible(false);
}
if(first_loop_after_window_activation){ if(first_loop_after_window_activation){
//std::cout<<"window active, first loop"<<std::endl; //std::cout<<"window active, first loop"<<std::endl;
@ -1364,6 +1416,8 @@ void the_game(
else{ else{
s32 dx = input->getMousePos().X - displaycenter.X; s32 dx = input->getMousePos().X - displaycenter.X;
s32 dy = input->getMousePos().Y - displaycenter.Y; s32 dy = input->getMousePos().Y - displaycenter.Y;
if(invert_mouse)
dy = -dy;
//std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl; //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
/*const float keyspeed = 500; /*const float keyspeed = 500;
@ -1384,6 +1438,8 @@ void the_game(
input->setMousePos(displaycenter.X, displaycenter.Y); input->setMousePos(displaycenter.X, displaycenter.Y);
} }
else{ else{
// Mac OSX gets upset if this is set every frame
if(device->getCursorControl()->isVisible() == false)
device->getCursorControl()->setVisible(true); device->getCursorControl()->setVisible(true);
//std::cout<<"window inactive"<<std::endl; //std::cout<<"window inactive"<<std::endl;
@ -1696,7 +1752,41 @@ void the_game(
{ {
std::cout<<DTIME<<"Ground right-clicked"<<std::endl; std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input) // If metadata provides an inventory view, activate it
if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
{
dstream<<DTIME<<"Launching custom inventory view"<<std::endl;
/*
Construct the unique identification string of the node
*/
std::string current_name;
current_name += "nodemeta:";
current_name += itos(nodepos.X);
current_name += ",";
current_name += itos(nodepos.Y);
current_name += ",";
current_name += itos(nodepos.Z);
/*
Create menu
*/
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
v2s16 invsize =
GUIInventoryMenu::makeDrawSpecArrayFromString(
draw_spec,
meta->getInventoryDrawSpecString(),
current_name);
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
&g_menumgr, invsize,
client.getInventoryContext(),
&client);
menu->setDrawSpec(draw_spec);
menu->drop();
}
else if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
{ {
dstream<<"Sign node right-clicked"<<std::endl; dstream<<"Sign node right-clicked"<<std::endl;
@ -1713,51 +1803,6 @@ void the_game(
&g_menumgr, dest, &g_menumgr, dest,
wtext))->drop(); wtext))->drop();
} }
else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
{
dstream<<"Chest node right-clicked"<<std::endl;
//ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
std::string chest_inv_id;
chest_inv_id += "nodemeta:";
chest_inv_id += itos(nodepos.X);
chest_inv_id += ",";
chest_inv_id += itos(nodepos.Y);
chest_inv_id += ",";
chest_inv_id += itos(nodepos.Z);
GUIInventoryMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1,
&g_menumgr, v2s16(8,9),
client.getInventoryContext(),
&client);
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", chest_inv_id, "0",
v2s32(0, 0), v2s32(8, 4)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", "current_player", "main",
v2s32(0, 5), v2s32(8, 4)));
menu->setDrawSpec(draw_spec);
menu->drop();
}
else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
{
dstream<<"Furnace node right-clicked"<<std::endl;
GUIFurnaceMenu *menu =
new GUIFurnaceMenu(guienv, guiroot, -1,
&g_menumgr, nodepos, &client);
menu->drop();
}
else else
{ {
client.groundAction(1, nodepos, neighbourpos, g_selected_item); client.groundAction(1, nodepos, neighbourpos, g_selected_item);
@ -1824,6 +1869,22 @@ void the_game(
0.05+brightness*0.95); 0.05+brightness*0.95);
} }
/*
Update farmesh
*/
if(farmesh)
{
farmesh_range = draw_control.wanted_range * 10;
if(draw_control.range_all && farmesh_range < 500)
farmesh_range = 500;
if(farmesh_range > 1000)
farmesh_range = 1000;
farmesh->step(dtime);
farmesh->update(v2f(player_position.X, player_position.Z),
0.05+brightness*0.95, farmesh_range);
}
// Store brightness value // Store brightness value
old_brightness = brightness; old_brightness = brightness;
@ -1833,11 +1894,19 @@ void the_game(
if(g_settings.getBool("enable_fog") == true) if(g_settings.getBool("enable_fog") == true)
{ {
f32 range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5; f32 range;
if(farmesh)
{
range = BS*farmesh_range;
}
else
{
range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5;
if(draw_control.range_all) if(draw_control.range_all)
range = 100000*BS; range = 100000*BS;
if(range < 50*BS) if(range < 50*BS)
range = range * 0.5 + 25*BS; range = range * 0.5 + 25*BS;
}
driver->setFog( driver->setFog(
bgcolor, bgcolor,
@ -1985,7 +2054,7 @@ void the_game(
10, 10,
50, 50,
screensize.X - 10, screensize.X - 10,
50 + text_height*chat_lines.size() 50 + guitext_chat->getTextHeight()
); );
guitext_chat->setRelativePosition(rect); guitext_chat->setRelativePosition(rect);
@ -2166,6 +2235,7 @@ void the_game(
/* /*
Drop stuff Drop stuff
*/ */
if(clouds)
clouds->drop(); clouds->drop();
/* /*

@ -1,58 +0,0 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "guiFurnaceMenu.h"
#include "client.h"
GUIFurnaceMenu::GUIFurnaceMenu(
gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
v3s16 nodepos,
Client *client
):
GUIInventoryMenu(env, parent, id, menumgr, v2s16(8,9),
client->getInventoryContext(), client),
m_nodepos(nodepos),
m_client(client)
{
std::string furnace_inv_id;
furnace_inv_id += "nodemeta:";
furnace_inv_id += itos(nodepos.X);
furnace_inv_id += ",";
furnace_inv_id += itos(nodepos.Y);
furnace_inv_id += ",";
furnace_inv_id += itos(nodepos.Z);
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "fuel",
v2s32(2, 3), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "src",
v2s32(2, 1), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "dst",
v2s32(5, 1), v2s32(2, 2)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", "current_player", "main",
v2s32(0, 5), v2s32(8, 4)));
setDrawSpec(draw_spec);
}

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiInventoryMenu.h" #include "guiInventoryMenu.h"
#include "constants.h" #include "constants.h"
#include "keycode.h" #include "keycode.h"
#include "strfnd.h"
void drawInventoryItem(video::IVideoDriver *driver, void drawInventoryItem(video::IVideoDriver *driver,
gui::IGUIFont *font, gui::IGUIFont *font,
@ -412,5 +413,85 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false; return Parent ? Parent->OnEvent(event) : false;
} }
/*
Here is an example traditional set-up sequence for a DrawSpec list:
std::string furnace_inv_id = "nodemetadata:0,1,2";
core::array<GUIInventoryMenu::DrawSpec> draw_spec;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "fuel",
v2s32(2, 3), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "src",
v2s32(2, 1), v2s32(1, 1)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", furnace_inv_id, "dst",
v2s32(5, 1), v2s32(2, 2)));
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
"list", "current_player", "main",
v2s32(0, 5), v2s32(8, 4)));
setDrawSpec(draw_spec);
Here is the string for creating the same DrawSpec list (a single line,
spread to multiple lines here):
GUIInventoryMenu::makeDrawSpecArrayFromString(
draw_spec,
"nodemetadata:0,1,2",
"invsize[8,9;]"
"list[current_name;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]"
"list[current_player;main;0,5;8,4;]");
Returns inventory menu size defined by invsize[].
*/
v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
const std::string &data,
const std::string &current_name)
{
v2s16 invsize(8,9);
Strfnd f(data);
while(f.atend() == false)
{
std::string type = trim(f.next("["));
//dstream<<"type="<<type<<std::endl;
if(type == "list")
{
std::string name = f.next(";");
if(name == "current_name")
name = current_name;
std::string subname = f.next(";");
s32 pos_x = stoi(f.next(","));
s32 pos_y = stoi(f.next(";"));
s32 geom_x = stoi(f.next(","));
s32 geom_y = stoi(f.next(";"));
dstream<<"list name="<<name<<", subname="<<subname
<<", pos=("<<pos_x<<","<<pos_y<<")"
<<", geom=("<<geom_x<<","<<geom_y<<")"
<<std::endl;
draw_spec.push_back(GUIInventoryMenu::DrawSpec(
type, name, subname,
v2s32(pos_x,pos_y),v2s32(geom_x,geom_y)));
f.next("]");
}
else if(type == "invsize")
{
invsize.X = stoi(f.next(","));
invsize.Y = stoi(f.next(";"));
dstream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
f.next("]");
}
else
{
// Ignore others
std::string ts = f.next("]");
dstream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
<<std::endl;
}
}
return invsize;
}

@ -103,6 +103,12 @@ public:
v2s32 geom; v2s32 geom;
}; };
// See .cpp for format
static v2s16 makeDrawSpecArrayFromString(
core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
const std::string &data,
const std::string &current_name);
GUIInventoryMenu(gui::IGUIEnvironment* env, GUIInventoryMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IMenuManager *menumgr,

@ -46,13 +46,14 @@ struct MainMenuData
{ {
MainMenuData(): MainMenuData():
// Client opts // Client opts
fancy_trees(false), smooth_lighting(false), fancy_trees(false),
smooth_lighting(false),
// Server opts // Server opts
creative_mode(false), enable_damage(false), creative_mode(false),
enable_damage(false),
// Actions // Actions
delete_map(false) delete_map(false)
{ {}
}
// These are in the native format of the gui elements // These are in the native format of the gui elements
@ -73,16 +74,13 @@ struct MainMenuData
class GUIMainMenu : public GUIModalMenu class GUIMainMenu : public GUIModalMenu
{ {
public: public:
GUIMainMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, GUIMainMenu(gui::IGUIEnvironment* env,
IMenuManager *menumgr, MainMenuData *data, gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
MainMenuData *data,
IGameCallback *gamecallback); IGameCallback *gamecallback);
~GUIMainMenu(); ~GUIMainMenu();
gui::IGUIEnvironment* env;
gui::IGUIElement* parent;
s32 id;
IMenuManager *menumgr;
void removeChildren(); void removeChildren();
/* /*
Remove and re-add (or reposition) stuff Remove and re-add (or reposition) stuff
@ -104,6 +102,11 @@ private:
MainMenuData *m_data; MainMenuData *m_data;
bool m_accepted; bool m_accepted;
IGameCallback *m_gamecallback; IGameCallback *m_gamecallback;
gui::IGUIEnvironment* env;
gui::IGUIElement* parent;
s32 id;
IMenuManager *menumgr;
}; };
#endif #endif

@ -28,8 +28,10 @@
class GUIMessageMenu : public GUIModalMenu class GUIMessageMenu : public GUIModalMenu
{ {
public: public:
GUIMessageMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, GUIMessageMenu(gui::IGUIEnvironment* env,
IMenuManager *menumgr, std::wstring message_text); gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
std::wstring message_text);
~GUIMessageMenu(); ~GUIMessageMenu();
void removeChildren(); void removeChildren();

@ -1,21 +1,19 @@
/* /*
Minetest-c55 Part of Minetest-c55
Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com> Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
This program is free software; you can redistribute it and/or modify Permission to use, copy, modify, and distribute this software for any
it under the terms of the GNU General Public License as published by purpose with or without fee is hereby granted, provided that the above
the Free Software Foundation; either version 2 of the License, or copyright notice and this permission notice appear in all copies.
(at your option) any later version.
This program is distributed in the hope that it will be useful, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
but WITHOUT ANY WARRANTY; without even the implied warranty of WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
GNU General Public License for more details. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
You should have received a copy of the GNU General Public License along ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
with this program; if not, write to the Free Software Foundation, Inc., OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "guiPasswordChange.h" #include "guiPasswordChange.h"

@ -1,21 +1,19 @@
/* /*
Minetest-c55 Part of Minetest-c55
Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com> Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
This program is free software; you can redistribute it and/or modify Permission to use, copy, modify, and distribute this software for any
it under the terms of the GNU General Public License as published by purpose with or without fee is hereby granted, provided that the above
the Free Software Foundation; either version 2 of the License, or copyright notice and this permission notice appear in all copies.
(at your option) any later version.
This program is distributed in the hope that it will be useful, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
but WITHOUT ANY WARRANTY; without even the implied warranty of WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
GNU General Public License for more details. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
You should have received a copy of the GNU General Public License along ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
with this program; if not, write to the Free Software Foundation, Inc., OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef GUIPASSWORDCHANGE_HEADER #ifndef GUIPASSWORDCHANGE_HEADER

@ -28,6 +28,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream> #include <sstream>
#include "main.h" #include "main.h"
#include "serverobject.h" #include "serverobject.h"
#include "content_mapnode.h"
#include "content_inventory.h"
/* /*
InventoryItem InventoryItem
@ -110,36 +112,12 @@ ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, u16 id, v3f
bool MaterialItem::isCookable() bool MaterialItem::isCookable()
{ {
if(m_content == CONTENT_TREE) return item_material_is_cookable(m_content);
{
return true;
}
else if(m_content == CONTENT_COBBLE)
{
return true;
}
else if(m_content == CONTENT_SAND)
{
return true;
}
return false;
} }
InventoryItem *MaterialItem::createCookResult() InventoryItem *MaterialItem::createCookResult()
{ {
if(m_content == CONTENT_TREE) return item_material_create_cook_result(m_content);
{
return new CraftItem("lump_of_coal", 1);
}
else if(m_content == CONTENT_COBBLE)
{
return new MaterialItem(CONTENT_STONE, 1);
}
else if(m_content == CONTENT_SAND)
{
return new MaterialItem(CONTENT_GLASS, 1);
}
return NULL;
} }
/* /*
@ -152,28 +130,7 @@ video::ITexture * CraftItem::getImage()
if(g_texturesource == NULL) if(g_texturesource == NULL)
return NULL; return NULL;
std::string name; std::string name = item_craft_get_image_name(m_subname);
if(m_subname == "Stick")
name = "stick.png";
else if(m_subname == "paper")
name = "paper.png";
else if(m_subname == "book")
name = "book.png";
else if(m_subname == "lump_of_coal")
name = "lump_of_coal.png";
else if(m_subname == "lump_of_iron")
name = "lump_of_iron.png";
else if(m_subname == "lump_of_clay")
name = "lump_of_clay.png";
else if(m_subname == "steel_ingot")
name = "steel_ingot.png";
else if(m_subname == "clay_brick")
name = "clay_brick.png";
else if(m_subname == "rat")
name = "rat.png";
else
name = "cloud.png";
// Get such a texture // Get such a texture
return g_texturesource->getTextureRaw(name); return g_texturesource->getTextureRaw(name);
@ -183,50 +140,35 @@ video::ITexture * CraftItem::getImage()
ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos) ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
{ {
// Special cases // Special cases
if(m_subname == "rat") ServerActiveObject *obj = item_craft_create_object(m_subname, env, id, pos);
{ if(obj)
ServerActiveObject *obj = new RatSAO(env, id, pos);
return obj; return obj;
}
// Default // Default
else
{
return InventoryItem::createSAO(env, id, pos); return InventoryItem::createSAO(env, id, pos);
} }
}
u16 CraftItem::getDropCount() u16 CraftItem::getDropCount()
{ {
// Special cases // Special cases
if(m_subname == "rat") s16 dc = item_craft_get_drop_count(m_subname);
return 1; if(dc != -1)
return dc;
// Default // Default
else
return InventoryItem::getDropCount(); return InventoryItem::getDropCount();
} }
bool CraftItem::isCookable() bool CraftItem::isCookable()
{ {
if(m_subname == "lump_of_iron" || m_subname == "lump_of_clay") return item_craft_is_cookable(m_subname);
{
return true;
}
return false;
} }
InventoryItem *CraftItem::createCookResult() InventoryItem *CraftItem::createCookResult()
{ {
if(m_subname == "lump_of_iron") return item_craft_create_cook_result(m_subname);
{
return new CraftItem("steel_ingot", 1);
}
else if(m_subname == "lump_of_clay")
return new CraftItem("clay_brick", 1);
return NULL;
} }
/* /*
MapBlockObjectItem MapBlockObjectItem DEPRECATED
TODO: Remove TODO: Remove
*/ */
#ifndef SERVER #ifndef SERVER
@ -528,7 +470,7 @@ InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
//setDirty(true); //setDirty(true);
// If it is an empty position, it's an easy job. // If it is an empty position, it's an easy job.
InventoryItem *to_item = m_items[i]; InventoryItem *to_item = getItem(i);
if(to_item == NULL) if(to_item == NULL)
{ {
m_items[i] = newitem; m_items[i] = newitem;
@ -560,7 +502,7 @@ InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
bool InventoryList::itemFits(u32 i, InventoryItem *newitem) bool InventoryList::itemFits(u32 i, InventoryItem *newitem)
{ {
// If it is an empty position, it's an easy job. // If it is an empty position, it's an easy job.
InventoryItem *to_item = m_items[i]; InventoryItem *to_item = getItem(i);
if(to_item == NULL) if(to_item == NULL)
{ {
return true; return true;
@ -586,7 +528,7 @@ InventoryItem * InventoryList::takeItem(u32 i, u32 count)
//setDirty(true); //setDirty(true);
InventoryItem *item = m_items[i]; InventoryItem *item = getItem(i);
// If it is an empty position, return NULL // If it is an empty position, return NULL
if(item == NULL) if(item == NULL)
return NULL; return NULL;

@ -19,6 +19,7 @@
#include "keycode.h" #include "keycode.h"
#include "main.h" // For g_settings #include "main.h" // For g_settings
#define CHECKKEY(x){if(strcmp(name, #x)==0) return irr::x;} #define CHECKKEY(x){if(strcmp(name, #x)==0) return irr::x;}
irr::EKEY_CODE keyname_to_keycode(const char *name) irr::EKEY_CODE keyname_to_keycode(const char *name)
@ -209,6 +210,7 @@ std::string keycode_to_keyname(s32 keycode)
{ {
return KeyNames[keycode]; return KeyNames[keycode];
} }
/* /*
Key config Key config
*/ */
@ -231,4 +233,3 @@ void clearKeyCache()
{ {
g_key_setting_cache.clear(); g_key_setting_cache.clear();
} }

@ -29,5 +29,6 @@ irr::EKEY_CODE keyname_to_keycode(const char *name);
irr::EKEY_CODE getKeySetting(const char *settingname); irr::EKEY_CODE getKeySetting(const char *settingname);
std::string keycode_to_keyname(s32 keycode); std::string keycode_to_keyname(s32 keycode);
void clearCache(); void clearCache();
#endif #endif

@ -89,6 +89,17 @@ SUGG: Make a system for pregenerating quick information for mapblocks, so
that the client can show them as cubes before they are actually sent that the client can show them as cubes before they are actually sent
or even generated. or even generated.
SUGG: Erosion simulation at map generation time
- Simulate water flows, which would carve out dirt fast and
then turn stone into gravel and sand and relocate it.
- How about relocating minerals, too? Coal and gold in
downstream sand and gravel would be kind of cool
- This would need a better way of handling minerals, mainly
to have mineral content as a separate field. the first
parameter field is free for this.
- Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom
Gaming ideas: Gaming ideas:
------------- -------------
@ -172,7 +183,15 @@ TODO: A setting for enabling bilinear filtering for textures
TODO: Better control of draw_control.wanted_max_blocks TODO: Better control of draw_control.wanted_max_blocks
TODO: Block mesh generator to tile properly on smooth lighting TODO: Further investigate the use of GPU lighting in addition to the
current one
TODO: Artificial (night) light could be more yellow colored than sunlight.
- This is technically doable.
- Also the actual colors of the textures could be made less colorful
in the dark but it's a bit more difficult.
SUGG: Somehow make the night less colorful
Configuration: Configuration:
-------------- --------------
@ -198,6 +217,9 @@ TODO: Don't update all meshes always on single node changes, but
FIXME: When disconnected to the menu, memory is not freed properly FIXME: When disconnected to the menu, memory is not freed properly
TODO: Investigate how much the mesh generator thread gets used when
transferring map data
Server: Server:
------- -------
@ -214,11 +236,20 @@ FIXME: Server sometimes goes into some infinite PeerNotFoundException loop
FIXME: The new optimized map sending doesn't sometimes send enough blocks FIXME: The new optimized map sending doesn't sometimes send enough blocks
from big caves and such from big caves and such
FIXME: Block send distance configuration does not take effect for some reason
TODO: Map saving should be done by EmergeThread
SUGG: Map unloading based on sector reference is not very good, it keeps
unnecessary stuff in memory. I guess. Investigate this.
TODO: When block is placed and it has param_type==CPT_FACEDIR_SIMPLE, set
the direction accordingly.
Environment: Environment:
------------ ------------
TODO: A list of "active blocks" in which stuff happens. TODO: A list of "active blocks" in which stuff happens. (+=done)
+ Add a never-resetted game timer to the server + Add a never-resetted game timer to the server
+ Add a timestamp value to blocks + Add a timestamp value to blocks
+ The simple rule: All blocks near some player are "active" + The simple rule: All blocks near some player are "active"
@ -260,31 +291,24 @@ SUGG: MovingObject::move and Player::move are basically the same.
- NOTE: There is a simple move implementation now in collision.{h,cpp} - NOTE: There is a simple move implementation now in collision.{h,cpp}
- NOTE: MovingObject will be deleted (MapBlockObject) - NOTE: MovingObject will be deleted (MapBlockObject)
TODO: Add a long step function to objects that is called with the time
difference when block activates
Map: Map:
---- ----
TODO: Mineral and ground material properties TODO: Mineral and ground material properties
- This way mineral ground toughness can be calculated with just - This way mineral ground toughness can be calculated with just
some formula, as well as tool strengths some formula, as well as tool strengths
- There are TODOs in appropriate files: material.h, content_mapnode.h
TODO: Flowing water to actually contain flow direction information TODO: Flowing water to actually contain flow direction information
- There is a space for this - it just has to be implemented. - There is a space for this - it just has to be implemented.
SUGG: Erosion simulation at map generation time
- Simulate water flows, which would carve out dirt fast and
then turn stone into gravel and sand and relocate it.
- How about relocating minerals, too? Coal and gold in
downstream sand and gravel would be kind of cool
- This would need a better way of handling minerals, mainly
to have mineral content as a separate field. the first
parameter field is free for this.
- Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom
SUGG: Try out the notch way of generating maps, that is, make bunches SUGG: Try out the notch way of generating maps, that is, make bunches
of low-res 3d noise and interpolate linearly. of low-res 3d noise and interpolate linearly.
Mapgen v2: Mapgen v2 (the current one):
* Possibly add some kind of erosion and other stuff * Possibly add some kind of erosion and other stuff
* Better water generation (spread it to underwater caverns but don't * Better water generation (spread it to underwater caverns but don't
fill dungeons that don't touch big water masses) fill dungeons that don't touch big water masses)
@ -293,25 +317,66 @@ Mapgen v2:
the other chunk making nasty straight walls when the other chunk the other chunk making nasty straight walls when the other chunk
is generated. Fix it. Maybe just a special case if the ground is is generated. Fix it. Maybe just a special case if the ground is
flat? flat?
* Consider not updating this one and make a good mainly block-based
generator
SUGG: Make two "modified states", one that forces the block to be saved at
the next save event, and one that makes the block to be saved at exit
time.
TODO: Add a not_fully_generated flag to MapBlock, which would be set for
blocks that contain eg. trees from neighboring generations but haven't
been generated itself. This is required for the future generator.
Misc. stuff: Misc. stuff:
------------ ------------
* Move digging property stuff from material.{h,cpp} to mapnode.cpp - Make sure server handles removing grass when a block is placed (etc)
- ...Or maybe move content_features to material.{h,cpp}? - The client should not do it by itself
- Block cube placement around player's head
- Protocol version field
- Consider getting some textures from cisoun's texture pack
- Ask from Cisoun
- Make sure the fence implementation and data format is good
- Think about using same bits for material for fences and doors, for
example
- Finish the ActiveBlockModifier stuff and use it for something
- Move mineral to param2, increment map serialization version, add conversion
TODO: Add a per-sector database to store surface stuff as simple flags/values
- Light?
- A building?
And at some point make the server send this data to the client too,
instead of referring to the noise functions
- Ground height
- Surface ground type
- Trees?
TODO: Restart irrlicht completely when coming back to main menu from game.
- This gets rid of everything that is stored in irrlicht's caches.
TODO: Merge bahamada's audio stuff (clean patch available)
TODO: Merge spongie's chest/furnace direction (by hand)
TODO: Merge key configuration menu (no clean patch available)
Making it more portable: Making it more portable:
------------------------ ------------------------
Stuff to do before release: Stuff to do before release:
--------------------------- ---------------------------
- Player default privileges and default password
- Chat privilege Fixes to the current release:
- Some simple block-based dynamic stuff in the world (finish the -----------------------------
ActiveBlockModifier stuff)
- Protocol version field Stuff to do after release:
- Consider getting some textures from cisoun's texture pack ---------------------------
- Add a long step function to objects that is called with the time
difference when block activates Doing currently:
----------------
TODO: Use MapBlock::resetUsageTimer() in appropriate places
(on client and server)
====================================================================== ======================================================================
@ -377,6 +442,9 @@ Settings g_settings;
// This is located in defaultsettings.cpp // This is located in defaultsettings.cpp
extern void set_default_settings(); extern void set_default_settings();
// Global profiler
Profiler g_profiler;
/* /*
Random stuff Random stuff
*/ */
@ -427,7 +495,14 @@ std::ostream *derr_client_ptr = &dstream;
class TimeGetter class TimeGetter
{ {
public: public:
TimeGetter(IrrlichtDevice *device): virtual u32 getTime() = 0;
};
// A precise irrlicht one
class IrrlichtTimeGetter: public TimeGetter
{
public:
IrrlichtTimeGetter(IrrlichtDevice *device):
m_device(device) m_device(device)
{} {}
u32 getTime() u32 getTime()
@ -439,8 +514,18 @@ public:
private: private:
IrrlichtDevice *m_device; IrrlichtDevice *m_device;
}; };
// Not so precise one which works without irrlicht
class SimpleTimeGetter: public TimeGetter
{
public:
u32 getTime()
{
return porting::getTimeMs();
}
};
// A pointer to a global instance of the time getter // A pointer to a global instance of the time getter
// TODO: why?
TimeGetter *g_timegetter = NULL; TimeGetter *g_timegetter = NULL;
u32 getTimeMs() u32 getTimeMs()
@ -994,6 +1079,15 @@ void drawMenuBackground(video::IVideoDriver* driver)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
/*
Initialization
*/
// Set locale. This is for forcing '.' as the decimal point.
std::locale::global(std::locale("C"));
// This enables printing all characters in bitmap font
setlocale(LC_CTYPE, "en_US");
/* /*
Parse command line Parse command line
*/ */
@ -1057,22 +1151,29 @@ int main(int argc, char *argv[])
disable_stderr = true; disable_stderr = true;
#endif #endif
porting::signal_handler_init();
bool &kill = *porting::signal_handler_killstatus();
// Initialize porting::path_data and porting::path_userdata
porting::initializePaths();
// Create user data directory
fs::CreateDir(porting::path_userdata);
// Initialize debug streams // Initialize debug streams
debugstreams_init(disable_stderr, DEBUGFILE); #ifdef RUN_IN_PLACE
std::string debugfile = DEBUGFILE;
#else
std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
#endif
debugstreams_init(disable_stderr, debugfile.c_str());
// Initialize debug stacks // Initialize debug stacks
debug_stacks_init(); debug_stacks_init();
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
porting::signal_handler_init(); // Init material properties table
bool &kill = *porting::signal_handler_killstatus(); //initializeMaterialProperties();
porting::initializePaths();
// Create user data directory
fs::CreateDir(porting::path_userdata);
// C-style stuff initialization
initializeMaterialProperties();
// Debug handler // Debug handler
BEGIN_DEBUG_EXCEPTION_HANDLER BEGIN_DEBUG_EXCEPTION_HANDLER
@ -1090,19 +1191,10 @@ int main(int argc, char *argv[])
// Initialize default settings // Initialize default settings
set_default_settings(); set_default_settings();
// Set locale. This is for forcing '.' as the decimal point.
std::locale::global(std::locale("C"));
// This enables printing all characters in bitmap font
setlocale(LC_CTYPE, "en_US");
// Initialize sockets // Initialize sockets
sockets_init(); sockets_init();
atexit(sockets_cleanup); atexit(sockets_cleanup);
/*
Initialization
*/
/* /*
Read config file Read config file
*/ */
@ -1188,7 +1280,7 @@ int main(int argc, char *argv[])
port = 30000; port = 30000;
// Map directory // Map directory
std::string map_dir = porting::path_userdata+"/map"; std::string map_dir = porting::path_userdata+"/world";
if(cmd_args.exists("map-dir")) if(cmd_args.exists("map-dir"))
map_dir = cmd_args.get("map-dir"); map_dir = cmd_args.get("map-dir");
else if(g_settings.exists("map-dir")) else if(g_settings.exists("map-dir"))
@ -1199,6 +1291,9 @@ int main(int argc, char *argv[])
{ {
DSTACK("Dedicated server branch"); DSTACK("Dedicated server branch");
// Create time getter
g_timegetter = new SimpleTimeGetter();
// Create server // Create server
Server server(map_dir.c_str()); Server server(map_dir.c_str());
server.start(port); server.start(port);
@ -1281,7 +1376,7 @@ int main(int argc, char *argv[])
device = device; device = device;
// Create time getter // Create time getter
g_timegetter = new TimeGetter(device); g_timegetter = new IrrlichtTimeGetter(device);
// Create game callback for menus // Create game callback for menus
g_gamecallback = new MainGameCallback(device); g_gamecallback = new MainGameCallback(device);
@ -1349,7 +1444,6 @@ int main(int argc, char *argv[])
Preload some textures and stuff Preload some textures and stuff
*/ */
init_content_inventory_texture_paths();
init_mapnode(); // Second call with g_texturesource set init_mapnode(); // Second call with g_texturesource set
init_mineral(); init_mineral();
@ -1488,12 +1582,21 @@ int main(int argc, char *argv[])
g_settings.set("creative_mode", itos(menudata.creative_mode)); g_settings.set("creative_mode", itos(menudata.creative_mode));
g_settings.set("enable_damage", itos(menudata.enable_damage)); g_settings.set("enable_damage", itos(menudata.enable_damage));
// Check for valid parameters, restart menu if invalid. // NOTE: These are now checked server side; no need to do it
// here, so let's not do it here.
/*// Check for valid parameters, restart menu if invalid.
if(playername == "") if(playername == "")
{ {
error_message = L"Name required."; error_message = L"Name required.";
continue; continue;
} }
// Check that name has only valid chars
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
{
error_message = L"Characters allowed: "
+narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
continue;
}*/
// Save settings // Save settings
g_settings.set("name", playername); g_settings.set("name", playername);

@ -28,6 +28,10 @@ extern Settings g_settings;
#include "tile.h" #include "tile.h"
extern ITextureSource *g_texturesource; extern ITextureSource *g_texturesource;
// Global profiler
#include "profiler.h"
extern Profiler g_profiler;
// Debug streams // Debug streams
#include <fstream> #include <fstream>

File diff suppressed because it is too large Load Diff

244
src/map.h

@ -41,13 +41,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapchunk.h" #include "mapchunk.h"
#include "nodemetadata.h" #include "nodemetadata.h"
namespace mapgen{
struct BlockMakeData;
};
/*
MapEditEvent
*/
#define MAPTYPE_BASE 0 #define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1 #define MAPTYPE_SERVER 1
#define MAPTYPE_CLIENT 2 #define MAPTYPE_CLIENT 2
enum MapEditEventType{ enum MapEditEventType{
// Node added (changed from air or something else to something)
MEET_ADDNODE, MEET_ADDNODE,
// Node removed (changed to air)
MEET_REMOVENODE, MEET_REMOVENODE,
// Node metadata of block changed (not knowing which node exactly)
// p stores block coordinate
MEET_BLOCK_NODE_METADATA_CHANGED,
// Anything else
MEET_OTHER MEET_OTHER
}; };
@ -122,7 +136,7 @@ public:
// On failure returns NULL // On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d); MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// On failure returns NULL // Same as the above (there exists no lock anymore)
MapSector * getSectorNoGenerateNoEx(v2s16 p2d); MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
// On failure throws InvalidPositionException // On failure throws InvalidPositionException
MapSector * getSectorNoGenerate(v2s16 p2d); MapSector * getSectorNoGenerate(v2s16 p2d);
@ -259,6 +273,9 @@ public:
virtual void save(bool only_changed){assert(0);}; virtual void save(bool only_changed){assert(0);};
// Server implements this
virtual void saveBlock(MapBlock *block){};
/* /*
Updates usage timers Updates usage timers
*/ */
@ -269,7 +286,7 @@ public:
void deleteSectors(core::list<v2s16> &list, bool only_blocks); void deleteSectors(core::list<v2s16> &list, bool only_blocks);
// Returns count of deleted sectors // Returns count of deleted sectors
u32 deleteUnusedSectors(float timeout, bool only_blocks=false, u32 unloadUnusedData(float timeout, bool only_blocks=false,
core::list<v3s16> *deleted_blocks=NULL); core::list<v3s16> *deleted_blocks=NULL);
// For debug printing // For debug printing
@ -320,8 +337,6 @@ protected:
This is the only map class that is able to generate map. This is the only map class that is able to generate map.
*/ */
struct ChunkMakeData;
class ServerMap : public Map class ServerMap : public Map
{ {
public: public:
@ -336,160 +351,25 @@ public:
return MAPTYPE_SERVER; return MAPTYPE_SERVER;
} }
/*
Map generation
*/
// Returns the position of the chunk where the sector is in
v2s16 sector_to_chunk(v2s16 sectorpos)
{
if(m_chunksize == 0)
return v2s16(0,0);
sectorpos.X += m_chunksize / 2;
sectorpos.Y += m_chunksize / 2;
v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
return chunkpos;
}
// Returns the position of the (0,0) sector of the chunk
v2s16 chunk_to_sector(v2s16 chunkpos)
{
if(m_chunksize == 0)
return v2s16(0,0);
v2s16 sectorpos(
chunkpos.X * m_chunksize,
chunkpos.Y * m_chunksize
);
sectorpos.X -= m_chunksize / 2;
sectorpos.Y -= m_chunksize / 2;
return sectorpos;
}
/*
Get a chunk.
*/
MapChunk *getChunk(v2s16 chunkpos)
{
core::map<v2s16, MapChunk*>::Node *n;
n = m_chunks.find(chunkpos);
if(n == NULL)
return NULL;
return n->getValue();
}
/*
True if the chunk and its neighbors are fully generated.
It means the chunk will not be touched in the future by the
generator. If false, generateChunk will make it true.
*/
bool chunkNonVolatile(v2s16 chunkpos)
{
if(m_chunksize == 0)
return true;
/*for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)*/
s16 x=0;
s16 y=0;
{
v2s16 chunkpos0 = chunkpos + v2s16(x,y);
MapChunk *chunk = getChunk(chunkpos);
if(chunk == NULL)
return false;
if(chunk->getGenLevel() != GENERATED_FULLY)
return false;
}
return true;
}
/*
Returns true if any chunk is marked as modified
*/
bool anyChunkModified()
{
for(core::map<v2s16, MapChunk*>::Iterator
i = m_chunks.getIterator();
i.atEnd()==false; i++)
{
v2s16 p = i.getNode()->getKey();
MapChunk *chunk = i.getNode()->getValue();
if(chunk->isModified())
return true;
}
return false;
}
void setChunksNonModified()
{
for(core::map<v2s16, MapChunk*>::Iterator
i = m_chunks.getIterator();
i.atEnd()==false; i++)
{
v2s16 p = i.getNode()->getKey();
MapChunk *chunk = i.getNode()->getValue();
chunk->setModified(false);
}
}
/*
Chunks are generated by using these and makeChunk().
*/
void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
MapChunk* finishChunkMake(ChunkMakeData &data,
core::map<v3s16, MapBlock*> &changed_blocks);
/*
Generate a chunk.
All chunks touching this one can be altered also.
*/
/*MapChunk* generateChunkRaw(v2s16 chunkpos,
core::map<v3s16, MapBlock*> &changed_blocks,
bool force=false);*/
/*
Generate a chunk and its neighbors so that it won't be touched
anymore.
*/
/*MapChunk* generateChunk(v2s16 chunkpos,
core::map<v3s16, MapBlock*> &changed_blocks);*/
/*
Generate a sector.
This is mainly called by generateChunkRaw.
*/
//ServerMapSector * generateSector(v2s16 p);
/* /*
Get a sector from somewhere. Get a sector from somewhere.
- Check memory - Check memory
- Check disk (loads blocks also) - Check disk (doesn't load blocks)
- Create blank one - Create blank one
*/ */
ServerMapSector * createSector(v2s16 p); ServerMapSector * createSector(v2s16 p);
/* /*
Get a sector from somewhere. Blocks are generated by using these and makeBlock().
- Check memory
- Check disk (loads blocks also)
- Generate chunk
*/ */
/*MapSector * emergeSector(v2s16 p, void initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos);
core::map<v3s16, MapBlock*> &changed_blocks);*/ MapBlock* finishBlockMake(mapgen::BlockMakeData *data,
core::map<v3s16, MapBlock*> &changed_blocks);
/*MapSector * emergeSector(v2s16 p)
{
core::map<v3s16, MapBlock*> changed_blocks;
return emergeSector(p, changed_blocks);
}*/
// A non-threaded wrapper to the above
MapBlock * generateBlock( MapBlock * generateBlock(
v3s16 p, v3s16 p,
MapBlock *original_dummy, core::map<v3s16, MapBlock*> &modified_blocks
ServerMapSector *sector,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
); );
/* /*
@ -499,36 +379,16 @@ public:
*/ */
MapBlock * createBlock(v3s16 p); MapBlock * createBlock(v3s16 p);
/*
only_from_disk, changed_blocks and lighting_invalidated_blocks
are not properly used by the new map generator.
*/
MapBlock * emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#if 0 #if 0
/* /*
NOTE: This comment might be outdated
Forcefully get a block from somewhere. Forcefully get a block from somewhere.
Exceptions: InvalidPositionException possible if only_from_disk==true
- InvalidPositionException: possible if only_from_disk==true
changed_blocks: Parameters:
- All already existing blocks that were modified are added. changed_blocks: Blocks that have been modified
- If found on disk, nothing will be added.
- If generated, the new block will not be included.
lighting_invalidated_blocks:
- All blocks that have heavy-to-calculate lighting changes
are added.
- updateLighting() should be called for these.
- A block that is in changed_blocks may not be in
lighting_invalidated_blocks.
*/ */
MapBlock * emergeBlock( MapBlock * emergeBlock(
v3s16 p, v3s16 p,
@ -551,6 +411,7 @@ public:
// dirname: final directory name // dirname: final directory name
v2s16 getSectorPos(std::string dirname); v2s16 getSectorPos(std::string dirname);
v3s16 getBlockPos(std::string sectordir, std::string blockfile); v3s16 getBlockPos(std::string sectordir, std::string blockfile);
static std::string getBlockFilename(v3s16 p);
void save(bool only_changed); void save(bool only_changed);
//void loadAll(); //void loadAll();
@ -559,8 +420,8 @@ public:
void saveMapMeta(); void saveMapMeta();
void loadMapMeta(); void loadMapMeta();
void saveChunkMeta(); /*void saveChunkMeta();
void loadChunkMeta(); void loadChunkMeta();*/
// The sector mutex should be locked when calling most of these // The sector mutex should be locked when calling most of these
@ -569,6 +430,7 @@ public:
// DEPRECATED? Sectors have no metadata anymore. // DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector); void saveSectorMeta(ServerMapSector *sector);
MapSector* loadSectorMeta(std::string dirname, bool save_after_load); MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
bool loadSectorMeta(v2s16 p2d);
// Full load of a sector including all blocks. // Full load of a sector including all blocks.
// returns true on success, false on failure. // returns true on success, false on failure.
@ -580,12 +442,15 @@ public:
void saveBlock(MapBlock *block); void saveBlock(MapBlock *block);
// This will generate a sector with getSector if not found. // This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false); void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
MapBlock* loadBlock(v3s16 p);
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
bool isSavingEnabled(){ return m_map_saving_enabled; } bool isSavingEnabled(){ return m_map_saving_enabled; }
u64 getSeed(){ return m_seed; }
private: private:
// Seed used for all kinds of randomness // Seed used for all kinds of randomness
u64 m_seed; u64 m_seed;
@ -593,11 +458,13 @@ private:
std::string m_savedir; std::string m_savedir;
bool m_map_saving_enabled; bool m_map_saving_enabled;
#if 0
// Chunk size in MapSectors // Chunk size in MapSectors
// If 0, chunks are disabled. // If 0, chunks are disabled.
s16 m_chunksize; s16 m_chunksize;
// Chunks // Chunks
core::map<v2s16, MapChunk*> m_chunks; core::map<v2s16, MapChunk*> m_chunks;
#endif
/* /*
Metadata is re-written on disk only if this is true. Metadata is re-written on disk only if this is true.
@ -732,6 +599,12 @@ public:
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
// Check if sector was drawn on last render()
bool sectorWasDrawn(v2s16 p)
{
return (m_last_drawn_sectors.find(p) != NULL);
}
private: private:
Client *m_client; Client *m_client;
@ -747,6 +620,7 @@ private:
v3f m_camera_direction; v3f m_camera_direction;
JMutex m_camera_mutex; JMutex m_camera_mutex;
core::map<v2s16, bool> m_last_drawn_sectors;
}; };
#endif #endif
@ -796,29 +670,5 @@ protected:
bool m_create_area; bool m_create_area;
}; };
struct ChunkMakeData
{
bool no_op;
ManualMapVoxelManipulator vmanip;
u64 seed;
v2s16 chunkpos;
s16 y_blocks_min;
s16 y_blocks_max;
v2s16 sectorpos_base;
s16 sectorpos_base_size;
v2s16 sectorpos_bigbase;
s16 sectorpos_bigbase_size;
s16 max_spread_amount;
UniqueQueue<v3s16> transforming_liquid;
ChunkMakeData():
no_op(false),
vmanip(NULL),
seed(0)
{}
};
void makeChunk(ChunkMakeData *data);
#endif #endif

File diff suppressed because it is too large Load Diff

@ -33,10 +33,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h" #include "voxel.h"
#include "nodemetadata.h" #include "nodemetadata.h"
#include "staticobject.h" #include "staticobject.h"
#include "mapblock_nodemod.h"
#ifndef SERVER
#include "mapblock_mesh.h"
#endif
#define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
// Named by looking towards z+ /*// Named by looking towards z+
enum{ enum{
FACE_BACK=0, FACE_BACK=0,
FACE_TOP, FACE_TOP,
@ -44,103 +49,37 @@ enum{
FACE_FRONT, FACE_FRONT,
FACE_BOTTOM, FACE_BOTTOM,
FACE_LEFT FACE_LEFT
};*/
enum ModifiedState
{
// Has not been modified.
MOD_STATE_CLEAN = 0,
MOD_RESERVED1 = 1,
// Has been modified, and will be saved when being unloaded.
MOD_STATE_WRITE_AT_UNLOAD = 2,
MOD_RESERVED3 = 3,
// Has been modified, and will be saved as soon as possible.
MOD_STATE_WRITE_NEEDED = 4,
MOD_RESERVED5 = 5,
}; };
struct FastFace // NOTE: If this is enabled, set MapBlock to be initialized with
// CONTENT_IGNORE.
/*enum BlockGenerationStatus
{ {
TileSpec tile; // Completely non-generated (filled with CONTENT_IGNORE).
video::S3DVertex vertices[4]; // Precalculated vertices BLOCKGEN_UNTOUCHED=0,
}; // Trees or similar might have been blitted from other blocks to here.
// Otherwise, the block contains CONTENT_IGNORE
enum NodeModType BLOCKGEN_FROM_NEIGHBORS=2,
{ // Has been generated, but some neighbors might put some stuff in here
NODEMOD_NONE, // when they are generated.
NODEMOD_CHANGECONTENT, //param is content id // Does not contain any CONTENT_IGNORE
NODEMOD_CRACK // param is crack progression BLOCKGEN_SELF_GENERATED=4,
}; // The block and all its neighbors have been generated
BLOCKGEN_FULLY_GENERATED=6
struct NodeMod };*/
{
NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
{
type = a_type;
param = a_param;
}
bool operator==(const NodeMod &other)
{
return (type == other.type && param == other.param);
}
enum NodeModType type;
u16 param;
};
class NodeModMap
{
public:
/*
returns true if the mod was different last time
*/
bool set(v3s16 p, const NodeMod &mod)
{
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
n->setValue(mod);
}
else
{
m_mods.insert(p, mod);
}
return true;
}
// Returns true if there was one
bool get(v3s16 p, NodeMod *mod)
{
core::map<v3s16, NodeMod>::Node *n;
n = m_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
}
bool clear(v3s16 p)
{
if(m_mods.find(p))
{
m_mods.remove(p);
return true;
}
return false;
}
bool clear()
{
if(m_mods.size() == 0)
return false;
m_mods.clear();
return true;
}
void copy(NodeModMap &dest)
{
dest.m_mods.clear();
for(core::map<v3s16, NodeMod>::Iterator
i = m_mods.getIterator();
i.atEnd() == false; i++)
{
dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
}
private:
core::map<v3s16, NodeMod> m_mods;
};
enum enum
{ {
@ -170,35 +109,6 @@ public:
} }
}; };
/*
Mesh making stuff
*/
class MapBlock;
#ifndef SERVER
struct MeshMakeData
{
u32 m_daynight_ratio;
NodeModMap m_temp_mods;
VoxelManipulator m_vmanip;
v3s16 m_blockpos;
/*
Copy central data directly from block, and other data from
parent of block.
*/
void fill(u32 daynight_ratio, MapBlock *block);
};
scene::SMesh* makeMapBlockMesh(MeshMakeData *data);
#endif
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir);
/* /*
MapBlock itself MapBlock itself
*/ */
@ -226,9 +136,10 @@ public:
u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
data = new MapNode[l]; data = new MapNode[l];
for(u32 i=0; i<l; i++){ for(u32 i=0; i<l; i++){
data[i] = MapNode(); //data[i] = MapNode();
data[i] = MapNode(CONTENT_IGNORE);
} }
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
/* /*
@ -250,19 +161,43 @@ public:
modified, so that the block is saved and possibly not deleted from modified, so that the block is saved and possibly not deleted from
memory. memory.
*/ */
// DEPRECATED, use *Modified()
void setChangedFlag() void setChangedFlag()
{ {
changed = true; //dstream<<"Deprecated setChangedFlag() called"<<std::endl;
raiseModified(MOD_STATE_WRITE_NEEDED);
} }
// DEPRECATED, use *Modified()
void resetChangedFlag() void resetChangedFlag()
{ {
changed = false; //dstream<<"Deprecated resetChangedFlag() called"<<std::endl;
resetModified();
} }
// DEPRECATED, use *Modified()
bool getChangedFlag() bool getChangedFlag()
{ {
return changed; //dstream<<"Deprecated getChangedFlag() called"<<std::endl;
if(getModified() == MOD_STATE_CLEAN)
return false;
else
return true;
} }
// m_modified methods
void raiseModified(u32 mod)
{
m_modified = MYMAX(m_modified, mod);
}
u32 getModified()
{
return m_modified;
}
void resetModified()
{
m_modified = MOD_STATE_CLEAN;
}
// is_underground getter/setter
bool getIsUnderground() bool getIsUnderground()
{ {
return is_underground; return is_underground;
@ -270,7 +205,7 @@ public:
void setIsUnderground(bool a_is_underground) void setIsUnderground(bool a_is_underground)
{ {
is_underground = a_is_underground; is_underground = a_is_underground;
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
#ifndef SERVER #ifndef SERVER
@ -288,22 +223,22 @@ public:
void setLightingExpired(bool expired) void setLightingExpired(bool expired)
{ {
m_lighting_expired = expired; m_lighting_expired = expired;
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
bool getLightingExpired() bool getLightingExpired()
{ {
return m_lighting_expired; return m_lighting_expired;
} }
/*bool isFullyGenerated() bool isGenerated()
{ {
return !m_not_fully_generated; return m_generated;
} }
void setFullyGenerated(bool b) void setGenerated(bool b)
{ {
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
m_not_fully_generated = !b; m_generated = b;
}*/ }
bool isValid() bool isValid()
{ {
@ -381,7 +316,7 @@ public:
if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException(); if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException(); if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n; data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
void setNode(v3s16 p, MapNode & n) void setNode(v3s16 p, MapNode & n)
@ -410,7 +345,7 @@ public:
if(data == NULL) if(data == NULL)
throw InvalidPositionException(); throw InvalidPositionException();
data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n; data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
void setNodeNoCheck(v3s16 p, MapNode & n) void setNodeNoCheck(v3s16 p, MapNode & n)
@ -474,8 +409,7 @@ public:
// See comments in mapblock.cpp // See comments in mapblock.cpp
bool propagateSunlight(core::map<v3s16, bool> & light_sources, bool propagateSunlight(core::map<v3s16, bool> & light_sources,
bool remove_light=false, bool *black_air_left=NULL, bool remove_light=false, bool *black_air_left=NULL);
bool grow_grass=false);
// Copies data to VoxelManipulator to getPosRelative() // Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst); void copyTo(VoxelManipulator &dst);
@ -487,36 +421,36 @@ public:
DEPRECATED DEPRECATED
*/ */
void serializeObjects(std::ostream &os, u8 version) /*void serializeObjects(std::ostream &os, u8 version)
{ {
m_objects.serialize(os, version); m_objects.serialize(os, version);
} }*/
// If smgr!=NULL, new objects are added to the scene // If smgr!=NULL, new objects are added to the scene
void updateObjects(std::istream &is, u8 version, void updateObjects(std::istream &is, u8 version,
scene::ISceneManager *smgr, u32 daynight_ratio) scene::ISceneManager *smgr, u32 daynight_ratio)
{ {
m_objects.update(is, version, smgr, daynight_ratio); m_objects.update(is, version, smgr, daynight_ratio);
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
void clearObjects() void clearObjects()
{ {
m_objects.clear(); m_objects.clear();
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
void addObject(MapBlockObject *object) void addObject(MapBlockObject *object)
throw(ContainerFullException, AlreadyExistsException) throw(ContainerFullException, AlreadyExistsException)
{ {
m_objects.add(object); m_objects.add(object);
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
void removeObject(s16 id) void removeObject(s16 id)
{ {
m_objects.remove(id); m_objects.remove(id);
setChangedFlag(); raiseModified(MOD_STATE_WRITE_NEEDED);
} }
MapBlockObject * getObject(s16 id) MapBlockObject * getObject(s16 id)
{ {
@ -626,13 +560,33 @@ public:
void setTimestamp(u32 time) void setTimestamp(u32 time)
{ {
m_timestamp = time; m_timestamp = time;
setChangedFlag(); raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
}
void setTimestampNoChangedFlag(u32 time)
{
m_timestamp = time;
} }
u32 getTimestamp() u32 getTimestamp()
{ {
return m_timestamp; return m_timestamp;
} }
/*
See m_usage_timer
*/
void resetUsageTimer()
{
m_usage_timer = 0;
}
void incrementUsageTimer(float dtime)
{
m_usage_timer += dtime;
}
u32 getUsageTimer()
{
return m_usage_timer;
}
/* /*
Serialization Serialization
*/ */
@ -698,10 +652,10 @@ private:
/* /*
- On the server, this is used for telling whether the - On the server, this is used for telling whether the
block has been changed from the one on disk. block has been modified from the one on disk.
- On the client, this is used for nothing. - On the client, this is used for nothing.
*/ */
bool changed; u32 m_modified;
/* /*
When propagating sunlight and the above block doesn't exist, When propagating sunlight and the above block doesn't exist,
@ -725,6 +679,8 @@ private:
// Whether day and night lighting differs // Whether day and night lighting differs
bool m_day_night_differs; bool m_day_night_differs;
bool m_generated;
// DEPRECATED // DEPRECATED
MapBlockObjectList m_objects; MapBlockObjectList m_objects;
@ -747,6 +703,12 @@ private:
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp. Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
*/ */
u32 m_timestamp; u32 m_timestamp;
/*
When the block is accessed, this is set to 0.
Map will unload the block when this reaches a timeout.
*/
float m_usage_timer;
}; };
inline bool blockpos_over_limit(v3s16 p) inline bool blockpos_over_limit(v3s16 p)

791
src/mapblock_mesh.cpp Normal file

@ -0,0 +1,791 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 "mapblock_mesh.h"
#include "light.h"
#include "mapblock.h"
#include "map.h"
#include "main.h" // For g_settings and g_texturesource
#include "content_mapblock.h"
void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
{
m_daynight_ratio = daynight_ratio;
m_blockpos = block->getPos();
v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
/*
There is no harm not copying the TempMods of the neighbors
because they are already copied to this block
*/
m_temp_mods.clear();
block->copyTempMods(m_temp_mods);
/*
Copy data
*/
// Allocate this block + neighbors
m_vmanip.clear();
m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
{
//TimeTaker timer("copy central block data");
// 0ms
// Copy our data
block->copyTo(m_vmanip);
}
{
//TimeTaker timer("copy neighbor block data");
// 0ms
/*
Copy neighbors. This is lightning fast.
Copying only the borders would be *very* slow.
*/
// Get map
NodeContainer *parentcontainer = block->getParent();
// This will only work if the parent is the map
assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
// OK, we have the map!
Map *map = (Map*)parentcontainer;
for(u16 i=0; i<6; i++)
{
const v3s16 &dir = g_6dirs[i];
v3s16 bp = m_blockpos + dir;
MapBlock *b = map->getBlockNoCreateNoEx(bp);
if(b)
b->copyTo(m_vmanip);
}
}
}
/*
vertex_dirs: v3s16[4]
*/
void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
{
/*
If looked from outside the node towards the face, the corners are:
0: bottom-right
1: bottom-left
2: top-left
3: top-right
*/
if(dir == v3s16(0,0,1))
{
// If looking towards z+, this is the face that is behind
// the center point, facing towards z+.
vertex_dirs[0] = v3s16(-1,-1, 1);
vertex_dirs[1] = v3s16( 1,-1, 1);
vertex_dirs[2] = v3s16( 1, 1, 1);
vertex_dirs[3] = v3s16(-1, 1, 1);
}
else if(dir == v3s16(0,0,-1))
{
// faces towards Z-
vertex_dirs[0] = v3s16( 1,-1,-1);
vertex_dirs[1] = v3s16(-1,-1,-1);
vertex_dirs[2] = v3s16(-1, 1,-1);
vertex_dirs[3] = v3s16( 1, 1,-1);
}
else if(dir == v3s16(1,0,0))
{
// faces towards X+
vertex_dirs[0] = v3s16( 1,-1, 1);
vertex_dirs[1] = v3s16( 1,-1,-1);
vertex_dirs[2] = v3s16( 1, 1,-1);
vertex_dirs[3] = v3s16( 1, 1, 1);
}
else if(dir == v3s16(-1,0,0))
{
// faces towards X-
vertex_dirs[0] = v3s16(-1,-1,-1);
vertex_dirs[1] = v3s16(-1,-1, 1);
vertex_dirs[2] = v3s16(-1, 1, 1);
vertex_dirs[3] = v3s16(-1, 1,-1);
}
else if(dir == v3s16(0,1,0))
{
// faces towards Y+ (assume Z- as "down" in texture)
vertex_dirs[0] = v3s16( 1, 1,-1);
vertex_dirs[1] = v3s16(-1, 1,-1);
vertex_dirs[2] = v3s16(-1, 1, 1);
vertex_dirs[3] = v3s16( 1, 1, 1);
}
else if(dir == v3s16(0,-1,0))
{
// faces towards Y- (assume Z+ as "down" in texture)
vertex_dirs[0] = v3s16( 1,-1, 1);
vertex_dirs[1] = v3s16(-1,-1, 1);
vertex_dirs[2] = v3s16(-1,-1,-1);
vertex_dirs[3] = v3s16( 1,-1,-1);
}
}
inline video::SColor lightColor(u8 alpha, u8 light)
{
return video::SColor(alpha,light,light,light);
}
struct FastFace
{
TileSpec tile;
video::S3DVertex vertices[4]; // Precalculated vertices
};
void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest)
{
FastFace face;
// Position is at the center of the cube.
v3f pos = p * BS;
posRelative_f *= BS;
v3f vertex_pos[4];
v3s16 vertex_dirs[4];
getNodeVertexDirs(dir, vertex_dirs);
for(u16 i=0; i<4; i++)
{
vertex_pos[i] = v3f(
BS/2*vertex_dirs[i].X,
BS/2*vertex_dirs[i].Y,
BS/2*vertex_dirs[i].Z
);
}
for(u16 i=0; i<4; i++)
{
vertex_pos[i].X *= scale.X;
vertex_pos[i].Y *= scale.Y;
vertex_pos[i].Z *= scale.Z;
vertex_pos[i] += pos + posRelative_f;
}
f32 abs_scale = 1.;
if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
v3f zerovector = v3f(0,0,0);
u8 alpha = tile.alpha;
/*u8 alpha = 255;
if(tile.id == TILE_WATER)
alpha = WATER_ALPHA;*/
float x0 = tile.texture.pos.X;
float y0 = tile.texture.pos.Y;
float w = tile.texture.size.X;
float h = tile.texture.size.Y;
/*video::SColor c = lightColor(alpha, li);
face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
core::vector2d<f32>(x0+w*abs_scale, y0+h));
face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
core::vector2d<f32>(x0, y0+h));
face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
core::vector2d<f32>(x0, y0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
core::vector2d<f32>(x0+w*abs_scale, y0));*/
face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
lightColor(alpha, li0),
core::vector2d<f32>(x0+w*abs_scale, y0+h));
face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
lightColor(alpha, li1),
core::vector2d<f32>(x0, y0+h));
face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
lightColor(alpha, li2),
core::vector2d<f32>(x0, y0));
face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
lightColor(alpha, li3),
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile;
//DEBUG
//f->tile = TILE_STONE;
dest.push_back(face);
}
/*
Gets node tile from any place relative to block.
Returns TILE_NODE if doesn't exist or should not be drawn.
*/
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap &temp_mods)
{
TileSpec spec;
spec = mn.getTile(face_dir);
/*
Check temporary modifications on this node
*/
/*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
// If modified
if(n != NULL)
{
struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT)
{
MapNode mn2(mod.param);
spec = mn2.getTile(face_dir);
}
if(mod.type == NODEMOD_CRACK)
{
/*
Get texture id, translate it to name, append stuff to
name, get texture id
*/
// Get original texture name
u32 orig_id = spec.texture.id;
std::string orig_name = g_texturesource->getTextureName(orig_id);
// Create new texture name
std::ostringstream os;
os<<orig_name<<"^[crack"<<mod.param;
// Get new texture
u32 new_id = g_texturesource->getTextureId(os.str());
/*dstream<<"MapBlock::getNodeTile(): Switching from "
<<orig_name<<" to "<<os.str()<<" ("
<<orig_id<<" to "<<new_id<<")"<<std::endl;*/
spec.texture = g_texturesource->getTexture(new_id);
}
}
return spec;
}
u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
{
/*
Check temporary modifications on this node
*/
/*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
// If modified
if(n != NULL)
{
struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT)
{
// Overrides content
return mod.param;
}
if(mod.type == NODEMOD_CRACK)
{
/*
Content doesn't change.
face_contents works just like it should, because
there should not be faces between differently cracked
nodes.
If a semi-transparent node is cracked in front an
another one, it really doesn't matter whether there
is a cracked face drawn in between or not.
*/
}
}
return mn.d;
}
v3s16 dirs8[8] = {
v3s16(0,0,0),
v3s16(0,0,1),
v3s16(0,1,0),
v3s16(0,1,1),
v3s16(1,0,0),
v3s16(1,1,0),
v3s16(1,0,1),
v3s16(1,1,1),
};
// Calculate lighting at the XYZ- corner of p
u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
{
u16 ambient_occlusion = 0;
u16 light = 0;
u16 light_count = 0;
for(u32 i=0; i<8; i++)
{
MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
if(content_features(n.d).param_type == CPT_LIGHT
// Fast-style leaves look better this way
&& content_features(n.d).solidness != 2)
{
light += decode_light(n.getLightBlend(daynight_ratio));
light_count++;
}
else
{
if(n.d != CONTENT_IGNORE)
ambient_occlusion++;
}
}
if(light_count == 0)
return 255;
light /= light_count;
if(ambient_occlusion > 4)
{
ambient_occlusion -= 4;
light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
}
return light;
}
// Calculate lighting at the given corner of p
u8 getSmoothLight(v3s16 p, v3s16 corner,
VoxelManipulator &vmanip, u32 daynight_ratio)
{
if(corner.X == 1) p.X += 1;
else assert(corner.X == -1);
if(corner.Y == 1) p.Y += 1;
else assert(corner.Y == -1);
if(corner.Z == 1) p.Z += 1;
else assert(corner.Z == -1);
return getSmoothLight(p, vmanip, daynight_ratio);
}
void getTileInfo(
// Input:
v3s16 blockpos_nodes,
v3s16 p,
v3s16 face_dir,
u32 daynight_ratio,
VoxelManipulator &vmanip,
NodeModMap &temp_mods,
bool smooth_lighting,
// Output:
bool &makes_face,
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
u8 *lights,
TileSpec &tile
)
{
MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
// This is hackish
u8 content0 = getNodeContent(p, n0, temp_mods);
u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
u8 mf = face_contents(content0, content1);
if(mf == 0)
{
makes_face = false;
return;
}
makes_face = true;
if(mf == 1)
{
tile = tile0;
p_corrected = p;
face_dir_corrected = face_dir;
}
else
{
tile = tile1;
p_corrected = p + face_dir;
face_dir_corrected = -face_dir;
}
if(smooth_lighting == false)
{
lights[0] = lights[1] = lights[2] = lights[3] =
decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
}
else
{
v3s16 vertex_dirs[4];
getNodeVertexDirs(face_dir_corrected, vertex_dirs);
for(u16 i=0; i<4; i++)
{
lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
vertex_dirs[i], vmanip, daynight_ratio);
}
}
return;
}
/*
startpos:
translate_dir: unit vector with only one of x, y or z
face_dir: unit vector with only one of x, y or z
*/
void updateFastFaceRow(
u32 daynight_ratio,
v3f posRelative_f,
v3s16 startpos,
u16 length,
v3s16 translate_dir,
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
core::array<FastFace> &dest,
NodeModMap &temp_mods,
VoxelManipulator &vmanip,
v3s16 blockpos_nodes,
bool smooth_lighting)
{
v3s16 p = startpos;
u16 continuous_tiles_count = 0;
bool makes_face;
v3s16 p_corrected;
v3s16 face_dir_corrected;
u8 lights[4];
TileSpec tile;
getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
vmanip, temp_mods, smooth_lighting,
makes_face, p_corrected, face_dir_corrected, lights, tile);
for(u16 j=0; j<length; j++)
{
// If tiling can be done, this is set to false in the next step
bool next_is_different = true;
v3s16 p_next;
bool next_makes_face = false;
v3s16 next_p_corrected;
v3s16 next_face_dir_corrected;
u8 next_lights[4] = {0,0,0,0};
TileSpec next_tile;
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
if(j != length - 1)
{
p_next = p + translate_dir;
getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
vmanip, temp_mods, smooth_lighting,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
next_tile);
if(next_makes_face == makes_face
&& next_p_corrected == p_corrected
&& next_face_dir_corrected == face_dir_corrected
&& next_lights[0] == lights[0]
&& next_lights[1] == lights[1]
&& next_lights[2] == lights[2]
&& next_lights[3] == lights[3]
&& next_tile == tile)
{
next_is_different = false;
}
}
continuous_tiles_count++;
// This is set to true if the texture doesn't allow more tiling
bool end_of_texture = false;
/*
If there is no texture, it can be tiled infinitely.
If tiled==0, it means the texture can be tiled infinitely.
Otherwise check tiled agains continuous_tiles_count.
*/
if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
{
if(tile.texture.tiled <= continuous_tiles_count)
end_of_texture = true;
}
// Do this to disable tiling textures
//end_of_texture = true; //DEBUG
if(next_is_different || end_of_texture)
{
/*
Create a face if there should be one
*/
if(makes_face)
{
// Floating point conversion of the position vector
v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
// Center point of face (kind of)
v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
v3f scale(1,1,1);
if(translate_dir.X != 0)
{
scale.X = continuous_tiles_count;
}
if(translate_dir.Y != 0)
{
scale.Y = continuous_tiles_count;
}
if(translate_dir.Z != 0)
{
scale.Z = continuous_tiles_count;
}
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
sp, face_dir_corrected, scale,
posRelative_f, dest);
}
continuous_tiles_count = 0;
makes_face = next_makes_face;
p_corrected = next_p_corrected;
face_dir_corrected = next_face_dir_corrected;
lights[0] = next_lights[0];
lights[1] = next_lights[1];
lights[2] = next_lights[2];
lights[3] = next_lights[3];
tile = next_tile;
}
p = p_next;
}
}
scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
{
// 4-21ms for MAP_BLOCKSIZE=16
// 24-155ms for MAP_BLOCKSIZE=32
//TimeTaker timer1("makeMapBlockMesh()");
core::array<FastFace> fastfaces_new;
v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
// floating point conversion
v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
/*
Some settings
*/
//bool new_style_water = g_settings.getBool("new_style_water");
//bool new_style_leaves = g_settings.getBool("new_style_leaves");
bool smooth_lighting = g_settings.getBool("smooth_lighting");
/*
We are including the faces of the trailing edges of the block.
This means that when something changes, the caller must
also update the meshes of the blocks at the leading edges.
NOTE: This is the slowest part of this method.
*/
{
// 4-23ms for MAP_BLOCKSIZE=16
//TimeTaker timer2("updateMesh() collect");
/*
Go through every y,z and get top(y+) faces in rows of x+
*/
for(s16 y=0; y<MAP_BLOCKSIZE; y++){
for(s16 z=0; z<MAP_BLOCKSIZE; z++){
updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
v3s16(0,y,z), MAP_BLOCKSIZE,
v3s16(1,0,0), //dir
v3f (1,0,0),
v3s16(0,1,0), //face dir
v3f (0,1,0),
fastfaces_new,
data->m_temp_mods,
data->m_vmanip,
blockpos_nodes,
smooth_lighting);
}
}
/*
Go through every x,y and get right(x+) faces in rows of z+
*/
for(s16 x=0; x<MAP_BLOCKSIZE; x++){
for(s16 y=0; y<MAP_BLOCKSIZE; y++){
updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
v3s16(x,y,0), MAP_BLOCKSIZE,
v3s16(0,0,1),
v3f (0,0,1),
v3s16(1,0,0),
v3f (1,0,0),
fastfaces_new,
data->m_temp_mods,
data->m_vmanip,
blockpos_nodes,
smooth_lighting);
}
}
/*
Go through every y,z and get back(z+) faces in rows of x+
*/
for(s16 z=0; z<MAP_BLOCKSIZE; z++){
for(s16 y=0; y<MAP_BLOCKSIZE; y++){
updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
v3s16(0,y,z), MAP_BLOCKSIZE,
v3s16(1,0,0),
v3f (1,0,0),
v3s16(0,0,1),
v3f (0,0,1),
fastfaces_new,
data->m_temp_mods,
data->m_vmanip,
blockpos_nodes,
smooth_lighting);
}
}
}
// End of slow part
/*
Convert FastFaces to SMesh
*/
MeshCollector collector;
if(fastfaces_new.size() > 0)
{
// avg 0ms (100ms spikes when loading textures the first time)
//TimeTaker timer2("updateMesh() mesh building");
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_FOG_ENABLE, true);
//material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
//material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
for(u32 i=0; i<fastfaces_new.size(); i++)
{
FastFace &f = fastfaces_new[i];
const u16 indices[] = {0,1,2,2,3,0};
const u16 indices_alternate[] = {0,1,3,2,3,1};
video::ITexture *texture = f.tile.texture.atlas;
if(texture == NULL)
continue;
material.setTexture(0, texture);
f.tile.applyMaterialOptions(material);
const u16 *indices_p = indices;
/*
Revert triangles for nicer looking gradient if vertices
1 and 3 have same color or 0 and 2 have different color.
*/
if(f.vertices[0].Color != f.vertices[2].Color
|| f.vertices[1].Color == f.vertices[3].Color)
indices_p = indices_alternate;
collector.append(material, f.vertices, 4, indices_p, 6);
}
}
/*
Add special graphics:
- torches
- flowing water
- fences
- whatever
*/
mapblock_mesh_generate_special(data, collector);
/*
Add stuff from collector to mesh
*/
scene::SMesh *mesh_new = NULL;
mesh_new = new scene::SMesh();
collector.fillMesh(mesh_new);
/*
Do some stuff to the mesh
*/
mesh_new->recalculateBoundingBox();
/*
Delete new mesh if it is empty
*/
if(mesh_new->getMeshBufferCount() == 0)
{
mesh_new->drop();
mesh_new = NULL;
}
if(mesh_new)
{
#if 0
// Usually 1-700 faces and 1-7 materials
std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<mesh_new->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;
#endif
// Use VBO for mesh (this just would set this for ever buffer)
// This will lead to infinite memory usage because or irrlicht.
//mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
/*
NOTE: If that is enabled, some kind of a queue to the main
thread should be made which would call irrlicht to delete
the hardware buffer and then delete the mesh
*/
}
return mesh_new;
//std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
}

143
src/mapblock_mesh.h Normal file

@ -0,0 +1,143 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 MAPBLOCK_MESH_HEADER
#define MAPBLOCK_MESH_HEADER
#include "common_irrlicht.h"
#include "mapblock_nodemod.h"
#include "voxel.h"
/*
Mesh making stuff
*/
/*
This is used because CMeshBuffer::append() is very slow
*/
struct PreMeshBuffer
{
video::SMaterial material;
core::array<u16> indices;
core::array<video::S3DVertex> vertices;
};
class MeshCollector
{
public:
void append(
video::SMaterial material,
const video::S3DVertex* const vertices,
u32 numVertices,
const u16* const indices,
u32 numIndices
)
{
PreMeshBuffer *p = NULL;
for(u32 i=0; i<m_prebuffers.size(); i++)
{
PreMeshBuffer &pp = m_prebuffers[i];
if(pp.material != material)
continue;
p = &pp;
break;
}
if(p == NULL)
{
PreMeshBuffer pp;
pp.material = material;
m_prebuffers.push_back(pp);
p = &m_prebuffers[m_prebuffers.size()-1];
}
u32 vertex_count = p->vertices.size();
for(u32 i=0; i<numIndices; i++)
{
u32 j = indices[i] + vertex_count;
if(j > 65535)
{
dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
// NOTE: Fix is to just add an another MeshBuffer
}
p->indices.push_back(j);
}
for(u32 i=0; i<numVertices; i++)
{
p->vertices.push_back(vertices[i]);
}
}
void fillMesh(scene::SMesh *mesh)
{
/*dstream<<"Filling mesh with "<<m_prebuffers.size()
<<" meshbuffers"<<std::endl;*/
for(u32 i=0; i<m_prebuffers.size(); i++)
{
PreMeshBuffer &p = m_prebuffers[i];
/*dstream<<"p.vertices.size()="<<p.vertices.size()
<<", p.indices.size()="<<p.indices.size()
<<std::endl;*/
// Create meshbuffer
// This is a "Standard MeshBuffer",
// it's a typedeffed CMeshBuffer<video::S3DVertex>
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
// Set material
buf->Material = p.material;
//((scene::SMeshBuffer*)buf)->Material = p.material;
// Use VBO
//buf->setHardwareMappingHint(scene::EHM_STATIC);
// Add to mesh
mesh->addMeshBuffer(buf);
// Mesh grabbed it
buf->drop();
buf->append(p.vertices.pointer(), p.vertices.size(),
p.indices.pointer(), p.indices.size());
}
}
private:
core::array<PreMeshBuffer> m_prebuffers;
};
class MapBlock;
struct MeshMakeData
{
u32 m_daynight_ratio;
NodeModMap m_temp_mods;
VoxelManipulator m_vmanip;
v3s16 m_blockpos;
/*
Copy central data directly from block, and other data from
parent of block.
*/
void fill(u32 daynight_ratio, MapBlock *block);
};
scene::SMesh* makeMapBlockMesh(MeshMakeData *data);
#endif

114
src/mapblock_nodemod.h Normal file

@ -0,0 +1,114 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 MAPBLOCK_NODEMOD_HEADER
#define MAPBLOCK_NODEMOD_HEADER
enum NodeModType
{
NODEMOD_NONE,
NODEMOD_CHANGECONTENT, //param is content id
NODEMOD_CRACK // param is crack progression
};
struct NodeMod
{
NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
{
type = a_type;
param = a_param;
}
bool operator==(const NodeMod &other)
{
return (type == other.type && param == other.param);
}
enum NodeModType type;
u16 param;
};
class NodeModMap
{
public:
/*
returns true if the mod was different last time
*/
bool set(v3s16 p, const NodeMod &mod)
{
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
n->setValue(mod);
}
else
{
m_mods.insert(p, mod);
}
return true;
}
// Returns true if there was one
bool get(v3s16 p, NodeMod *mod)
{
core::map<v3s16, NodeMod>::Node *n;
n = m_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
}
bool clear(v3s16 p)
{
if(m_mods.find(p))
{
m_mods.remove(p);
return true;
}
return false;
}
bool clear()
{
if(m_mods.size() == 0)
return false;
m_mods.clear();
return true;
}
void copy(NodeModMap &dest)
{
dest.m_mods.clear();
for(core::map<v3s16, NodeMod>::Iterator
i = m_mods.getIterator();
i.atEnd() == false; i++)
{
dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
}
private:
core::map<v3s16, NodeMod> m_mods;
};
#endif

2007
src/mapgen.cpp Normal file

File diff suppressed because it is too large Load Diff

66
src/mapgen.h Normal file

@ -0,0 +1,66 @@
/*
Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 MAPGEN_HEADER
#define MAPGEN_HEADER
#include "common_irrlicht.h"
#include "utility.h" // UniqueQueue
struct BlockMakeData;
class MapBlock;
class ManualMapVoxelManipulator;
namespace mapgen
{
// Finds precise ground level at any position
s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
// Find out if block is completely underground
bool block_is_underground(u64 seed, v3s16 blockpos);
// Main map generation routine
void make_block(BlockMakeData *data);
// Add objects according to block content
void add_random_objects(MapBlock *block);
/*
These are used by FarMesh
*/
bool get_have_sand(u64 seed, v2s16 p2d);
double tree_amount_2d(u64 seed, v2s16 p);
struct BlockMakeData
{
bool no_op;
ManualMapVoxelManipulator *vmanip;
u64 seed;
v3s16 blockpos;
UniqueQueue<v3s16> transforming_liquid;
BlockMakeData();
~BlockMakeData();
};
}; // namespace mapgen
#endif

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mineral.h" #include "mineral.h"
// For g_settings // For g_settings
#include "main.h" #include "main.h"
#include "content_mapnode.h"
#include "nodemetadata.h" #include "nodemetadata.h"
ContentFeatures::~ContentFeatures() ContentFeatures::~ContentFeatures()
@ -107,9 +108,9 @@ void init_mapnode()
"g_texturesource!=NULL"<<std::endl; "g_texturesource!=NULL"<<std::endl;
} }
// Read some settings /*// Read some settings
bool new_style_water = g_settings.getBool("new_style_water"); bool new_style_water = g_settings.getBool("new_style_water");
bool new_style_leaves = g_settings.getBool("new_style_leaves"); bool new_style_leaves = g_settings.getBool("new_style_leaves");*/
/* /*
Initialize content feature table Initialize content feature table
@ -131,313 +132,17 @@ void init_mapnode()
{ {
ContentFeatures *f = &g_content_features[i]; ContentFeatures *f = &g_content_features[i];
// Re-initialize // Re-initialize
*f = ContentFeatures(); f->reset();
for(u16 j=0; j<6; j++) for(u16 j=0; j<6; j++)
f->tiles[j].material_type = initial_material_type; f->tiles[j].material_type = initial_material_type;
} }
u8 i; /*
ContentFeatures *f = NULL; Initialize mapnode content
*/
content_mapnode_init();
i = CONTENT_STONE;
f = &g_content_features[i];
f->setAllTextures("stone.png");
f->setInventoryTextureCube("stone.png", "stone.png", "stone.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 1";
i = CONTENT_GRASS;
f = &g_content_features[i];
f->setAllTextures("mud.png^grass_side.png");
f->setTexture(0, "grass.png");
f->setTexture(1, "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
i = CONTENT_GRASS_FOOTSTEPS;
f = &g_content_features[i];
f->setAllTextures("mud.png^grass_side.png");
f->setTexture(0, "grass_footsteps.png");
f->setTexture(1, "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_MUD)+" 1";
i = CONTENT_MUD;
f = &g_content_features[i];
f->setAllTextures("mud.png");
f->setInventoryTextureCube("mud.png", "mud.png", "mud.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_SAND;
f = &g_content_features[i];
f->setAllTextures("sand.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_SANDSTONE;
f = &g_content_features[i];
f->setAllTextures("sandstone.png");
f->setInventoryTextureCube("sandstone.png", "sandstone.png", "sandstone.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_SAND)+" 1";
i = CONTENT_CLAY;
f = &g_content_features[i];
f->setAllTextures("clay.png");
f->setInventoryTextureCube("clay.png", "clay.png", "clay.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("CraftItem lump_of_clay 4");
i = CONTENT_BRICK;
f = &g_content_features[i];
f->setAllTextures("brick.png");
f->setInventoryTextureCube("brick.png", "brick.png", "brick.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("CraftItem clay_brick 4");
i = CONTENT_TREE;
f = &g_content_features[i];
f->setAllTextures("tree.png");
f->setTexture(0, "tree_top.png");
f->setTexture(1, "tree_top.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_LEAVES;
f = &g_content_features[i];
f->light_propagates = true;
//f->param_type = CPT_MINERAL;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
if(new_style_leaves)
{
f->solidness = 0; // drawn separately, makes no faces
f->setInventoryTextureCube("leaves.png", "leaves.png", "leaves.png");
}
else
{
f->setAllTextures("[noalpha:leaves.png");
}
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_CACTUS;
f = &g_content_features[i];
f->setAllTextures("cactus_side.png");
f->setTexture(0, "cactus_top.png");
f->setTexture(1, "cactus_top.png");
f->setInventoryTextureCube("cactus_top.png", "cactus_side.png", "cactus_side.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_PAPYRUS;
f = &g_content_features[i];
f->setInventoryTexture("papyrus.png");
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
i = CONTENT_BOOKSHELF;
f = &g_content_features[i];
f->setAllTextures("bookshelf.png");
f->setTexture(0, "wood.png");
f->setTexture(1, "wood.png");
// FIXME: setInventoryTextureCube() only cares for the first texture
f->setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png");
//f->setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png");
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_GLASS;
f = &g_content_features[i];
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->setInventoryTextureCube("glass.png", "glass.png", "glass.png");
i = CONTENT_FENCE;
f = &g_content_features[i];
f->setInventoryTexture("fence.png");
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->air_equivalent = true; // grass grows underneath
i = CONTENT_RAIL;
f = &g_content_features[i];
f->setInventoryTexture("rail.png");
f->light_propagates = true;
f->param_type = CPT_LIGHT;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->solidness = 0; // drawn separately, makes no faces
f->air_equivalent = true; // grass grows underneath
f->walkable = false;
// Deprecated
i = CONTENT_COALSTONE;
f = &g_content_features[i];
//f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
f->setAllTextures("stone.png^mineral_coal.png");
f->is_ground_content = true;
i = CONTENT_WOOD;
f = &g_content_features[i];
f->setAllTextures("wood.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_MESE;
f = &g_content_features[i];
f->setAllTextures("mese.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_CLOUD;
f = &g_content_features[i];
f->setAllTextures("cloud.png");
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_AIR;
f = &g_content_features[i];
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0;
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->air_equivalent = true;
i = CONTENT_WATER;
f = &g_content_features[i];
f->setInventoryTextureCube("water.png", "water.png", "water.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->solidness = 0; // Drawn separately, makes no faces
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->liquid_type = LIQUID_FLOWING;
i = CONTENT_WATERSOURCE;
f = &g_content_features[i];
f->setInventoryTexture("water.png");
if(new_style_water)
{
f->solidness = 0; // drawn separately, makes no faces
}
else // old style
{
f->solidness = 1;
TileSpec t;
if(g_texturesource)
t.texture = g_texturesource->getTexture("water.png");
t.alpha = WATER_ALPHA;
t.material_type = MATERIAL_ALPHA_VERTEX;
t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
f->setAllTiles(t);
}
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->walkable = false;
f->pointable = false;
f->diggable = false;
f->buildable_to = true;
f->liquid_type = LIQUID_SOURCE;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_TORCH;
f = &g_content_features[i];
f->setInventoryTexture("torch_on_floor.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
f->wall_mounted = true;
f->air_equivalent = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_SIGN_WALL;
f = &g_content_features[i];
f->setInventoryTexture("sign_wall.png");
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->sunlight_propagates = true;
f->solidness = 0; // drawn separately, makes no faces
f->walkable = false;
f->wall_mounted = true;
f->air_equivalent = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
if(f->initial_metadata == NULL)
f->initial_metadata = new SignNodeMetadata("Some sign");
i = CONTENT_CHEST;
f = &g_content_features[i];
f->param_type = CPT_FACEDIR_SIMPLE;
f->setAllTextures("chest_side.png");
f->setTexture(0, "chest_top.png");
f->setTexture(1, "chest_top.png");
f->setTexture(5, "chest_front.png"); // Z-
f->setInventoryTexture("chest_top.png");
//f->setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png");
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
if(f->initial_metadata == NULL)
f->initial_metadata = new ChestNodeMetadata();
i = CONTENT_FURNACE;
f = &g_content_features[i];
f->param_type = CPT_FACEDIR_SIMPLE;
f->setAllTextures("furnace_side.png");
f->setTexture(5, "furnace_front.png"); // Z-
f->setInventoryTexture("furnace_front.png");
//f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
f->dug_item = std::string("MaterialItem ")+itos(CONTENT_COBBLE)+" 6";
if(f->initial_metadata == NULL)
f->initial_metadata = new FurnaceNodeMetadata();
i = CONTENT_COBBLE;
f = &g_content_features[i];
f->setAllTextures("cobble.png");
f->setInventoryTextureCube("cobble.png", "cobble.png", "cobble.png");
f->param_type = CPT_NONE;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
i = CONTENT_STEEL;
f = &g_content_features[i];
f->setAllTextures("steel_block.png");
f->setInventoryTextureCube("steel_block.png", "steel_block.png",
"steel_block.png");
f->param_type = CPT_NONE;
f->is_ground_content = true;
f->dug_item = std::string("MaterialItem ")+itos(i)+" 1";
// NOTE: Remember to add frequently used stuff to the texture atlas in tile.cpp
} }
v3s16 facedir_rotate(u8 facedir, v3s16 dir) v3s16 facedir_rotate(u8 facedir, v3s16 dir)
@ -525,16 +230,54 @@ u8 MapNode::getMineral()
return MINERAL_NONE; return MINERAL_NONE;
} }
// Pointers to c_str()s g_content_features[i].inventory_image_path /*
//const char * g_content_inventory_texture_paths[USEFUL_CONTENT_COUNT] = {0}; Gets lighting value at face of node
void init_content_inventory_texture_paths() Parameters must consist of air and !air.
Order doesn't matter.
If either of the nodes doesn't exist, light is 0.
parameters:
daynight_ratio: 0...1000
n: getNodeParent(p)
n2: getNodeParent(p + face_dir)
face_dir: axis oriented unit vector from p to p2
returns encoded light value.
*/
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir)
{ {
dstream<<"DEPRECATED "<<__FUNCTION_NAME<<std::endl; try{
/*for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++) u8 light;
u8 l1 = n.getLightBlend(daynight_ratio);
u8 l2 = n2.getLightBlend(daynight_ratio);
if(l1 > l2)
light = l1;
else
light = l2;
// Make some nice difference to different sides
// This makes light come from a corner
/*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
light = diminish_light(diminish_light(light));
else if(face_dir.X == -1 || face_dir.Z == -1)
light = diminish_light(light);*/
// All neighboring faces have different shade (like in minecraft)
if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
light = diminish_light(diminish_light(light));
else if(face_dir.Z == 1 || face_dir.Z == -1)
light = diminish_light(light);
return light;
}
catch(InvalidPositionException &e)
{ {
g_content_inventory_texture_paths[i] = return 0;
g_content_features[i].inventory_image_path.c_str(); }
}*/
} }

@ -27,6 +27,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h" #include "exceptions.h"
#include "serialization.h" #include "serialization.h"
#include "tile.h" #include "tile.h"
#include "materials.h"
/*
Naming scheme:
- Material = irrlicht's Material class
- Content = (u8) content of a node
- Tile = TileSpec at some side of a node of some content type
*/
/* /*
Initializes all kind of stuff in here. Initializes all kind of stuff in here.
@ -42,13 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
void init_mapnode(); void init_mapnode();
// Initializes g_content_inventory_texture_paths
void init_content_inventory_texture_paths();
// NOTE: This is not used appropriately everywhere.
#define MATERIALS_COUNT 256
/* /*
Ignored node. Ignored node.
@ -67,48 +68,6 @@ void init_content_inventory_texture_paths();
*/ */
#define CONTENT_AIR 254 #define CONTENT_AIR 254
/*
Suggested materials:
- Gravel
- Sand
New naming scheme:
- Material = irrlicht's Material class
- Content = (u8) content of a node
- Tile = (u16) Material ID at some side of a node
*/
#define CONTENT_STONE 0
#define CONTENT_GRASS 1
#define CONTENT_WATER 2
#define CONTENT_TORCH 3
#define CONTENT_TREE 4
#define CONTENT_LEAVES 5
#define CONTENT_GRASS_FOOTSTEPS 6
#define CONTENT_MESE 7
#define CONTENT_MUD 8
#define CONTENT_WATERSOURCE 9
// Pretty much useless, clouds won't be drawn this way
#define CONTENT_CLOUD 10
#define CONTENT_COALSTONE 11
#define CONTENT_WOOD 12
#define CONTENT_SAND 13
#define CONTENT_SIGN_WALL 14
#define CONTENT_CHEST 15
#define CONTENT_FURNACE 16
//#define CONTENT_WORKBENCH 17
#define CONTENT_COBBLE 18
#define CONTENT_STEEL 19
#define CONTENT_GLASS 20
#define CONTENT_FENCE 21
#define CONTENT_SANDSTONE 22
#define CONTENT_CACTUS 23
#define CONTENT_BRICK 24
#define CONTENT_CLAY 25
#define CONTENT_PAPYRUS 26
#define CONTENT_BOOKSHELF 27
#define CONTENT_RAIL 28
/* /*
Content feature list Content feature list
*/ */
@ -181,9 +140,19 @@ struct ContentFeatures
// Initial metadata is cloned from this // Initial metadata is cloned from this
NodeMetadata *initial_metadata; NodeMetadata *initial_metadata;
//TODO: Move more properties here // If the content is liquid, this is the flowing version of the liquid.
// If content is liquid, this is the same content.
u8 liquid_alternative_flowing;
ContentFeatures() // Amount of light the node emits
u8 light_source;
// Digging properties for different tools
DiggingPropertiesList digging_properties;
// NOTE: Move relevant properties to here from elsewhere
void reset()
{ {
translate_to = NULL; translate_to = NULL;
param_type = CPT_NONE; param_type = CPT_NONE;
@ -201,6 +170,14 @@ struct ContentFeatures
air_equivalent = false; air_equivalent = false;
dug_item = ""; dug_item = "";
initial_metadata = NULL; initial_metadata = NULL;
liquid_alternative_flowing = CONTENT_IGNORE;
light_source = 0;
digging_properties.clear();
}
ContentFeatures()
{
reset();
} }
~ContentFeatures(); ~ContentFeatures();
@ -242,6 +219,11 @@ struct ContentFeatures
*/ */
ContentFeatures & content_features(u8 i); ContentFeatures & content_features(u8 i);
/*
Here is a bunch of DEPRECATED functions.
*/
/* /*
If true, the material allows light propagation and brightness is stored If true, the material allows light propagation and brightness is stored
in param. in param.
@ -250,9 +232,7 @@ ContentFeatures & content_features(u8 i);
inline bool light_propagates_content(u8 m) inline bool light_propagates_content(u8 m)
{ {
return content_features(m).light_propagates; return content_features(m).light_propagates;
//return (m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
} }
/* /*
If true, the material allows lossless sunlight propagation. If true, the material allows lossless sunlight propagation.
NOTE: It doesn't seem to go through torches regardlessly of this NOTE: It doesn't seem to go through torches regardlessly of this
@ -261,9 +241,7 @@ inline bool light_propagates_content(u8 m)
inline bool sunlight_propagates_content(u8 m) inline bool sunlight_propagates_content(u8 m)
{ {
return content_features(m).sunlight_propagates; return content_features(m).sunlight_propagates;
//return (m == CONTENT_AIR || m == CONTENT_TORCH);
} }
/* /*
On a node-node surface, the material of the node with higher solidness On a node-node surface, the material of the node with higher solidness
is used for drawing. is used for drawing.
@ -275,83 +253,54 @@ inline bool sunlight_propagates_content(u8 m)
inline u8 content_solidness(u8 m) inline u8 content_solidness(u8 m)
{ {
return content_features(m).solidness; return content_features(m).solidness;
/*// As of now, every pseudo node like torches are added to this
if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
return 0;
if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE)
return 1;
return 2;*/
} }
// Objects collide with walkable contents // Objects collide with walkable contents
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_walkable(u8 m) inline bool content_walkable(u8 m)
{ {
return content_features(m).walkable; return content_features(m).walkable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
} }
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_liquid(u8 m) inline bool content_liquid(u8 m)
{ {
return content_features(m).liquid_type != LIQUID_NONE; return content_features(m).liquid_type != LIQUID_NONE;
//return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
} }
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_flowing_liquid(u8 m) inline bool content_flowing_liquid(u8 m)
{ {
return content_features(m).liquid_type == LIQUID_FLOWING; return content_features(m).liquid_type == LIQUID_FLOWING;
//return (m == CONTENT_WATER);
} }
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_liquid_source(u8 m) inline bool content_liquid_source(u8 m)
{ {
return content_features(m).liquid_type == LIQUID_SOURCE; return content_features(m).liquid_type == LIQUID_SOURCE;
//return (m == CONTENT_WATERSOURCE);
} }
// CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER // CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER
// CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA // CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA
// NOTE: Don't use, use "content_features(m).whatever" instead
inline u8 make_liquid_flowing(u8 m) inline u8 make_liquid_flowing(u8 m)
{ {
if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE) u8 c = content_features(m).liquid_alternative_flowing;
return CONTENT_WATER; assert(c != CONTENT_IGNORE);
assert(0); return c;
} }
// Pointable contents can be pointed to in the map // Pointable contents can be pointed to in the map
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_pointable(u8 m) inline bool content_pointable(u8 m)
{ {
return content_features(m).pointable; return content_features(m).pointable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
} }
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_diggable(u8 m) inline bool content_diggable(u8 m)
{ {
return content_features(m).diggable; return content_features(m).diggable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
} }
// NOTE: Don't use, use "content_features(m).whatever" instead // NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_buildable_to(u8 m) inline bool content_buildable_to(u8 m)
{ {
return content_features(m).buildable_to; return content_features(m).buildable_to;
//return (m == CONTENT_AIR || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
} }
/*
Returns true for contents that form the base ground that
follows the main heightmap
*/
/*inline bool is_ground_content(u8 m)
{
return content_features(m).is_ground_content;
}*/
/* /*
Nodes make a face if contents differ and solidness differs. Nodes make a face if contents differ and solidness differs.
Return value: Return value:
@ -501,30 +450,24 @@ struct MapNode
&& param2 == other.param2); && param2 == other.param2);
} }
/*
These four are DEPRECATED I guess. -c55
*/
bool light_propagates() bool light_propagates()
{ {
return light_propagates_content(d); return light_propagates_content(d);
} }
bool sunlight_propagates() bool sunlight_propagates()
{ {
return sunlight_propagates_content(d); return sunlight_propagates_content(d);
} }
u8 solidness() u8 solidness()
{ {
return content_solidness(d); return content_solidness(d);
} }
u8 light_source() u8 light_source()
{ {
/* return content_features(d).light_source;
Note that a block that isn't light_propagates() can be a light source.
*/
if(d == CONTENT_TORCH)
return LIGHT_MAX;
return 0;
} }
u8 getLightBanksWithSource() u8 getLightBanksWithSource()
@ -544,11 +487,6 @@ struct MapNode
return (lightday&0x0f) | ((lightnight<<4)&0xf0); return (lightday&0x0f) | ((lightnight<<4)&0xf0);
} }
void setLightBanks(u8 a_light)
{
param = a_light;
}
u8 getLight(enum LightBank bank) u8 getLight(enum LightBank bank)
{ {
// Select the brightest of [light source, propagated light] // Select the brightest of [light source, propagated light]
@ -613,13 +551,25 @@ struct MapNode
} }
// In mapnode.cpp // In mapnode.cpp
/*
Get tile of a face of the node.
dir: direction of face
Returns: TileSpec. Can contain miscellaneous texture coordinates,
which must be obeyed so that the texture atlas can be used.
*/
TileSpec getTile(v3s16 dir); TileSpec getTile(v3s16 dir);
/*
Gets mineral content of node, if there is any.
MINERAL_NONE if doesn't contain or isn't able to contain mineral.
*/
u8 getMineral(); u8 getMineral();
/* /*
These serialization functions are used when informing client These serialization functions are used when informing client
of a single node add of a single node add.
NOTE: When loading a MapBlock, these are not used. Should they?
*/ */
static u32 serializedLength(u8 version) static u32 serializedLength(u8 version)
@ -698,7 +648,24 @@ struct MapNode
} }
}; };
/*
Gets lighting value at face of node
Parameters must consist of air and !air.
Order doesn't matter.
If either of the nodes doesn't exist, light is 0.
parameters:
daynight_ratio: 0...1000
n: getNodeParent(p)
n2: getNodeParent(p + face_dir)
face_dir: axis oriented unit vector from p to p2
returns encoded light value.
*/
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir);
#endif #endif

@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
MapSector::MapSector(NodeContainer *parent, v2s16 pos): MapSector::MapSector(NodeContainer *parent, v2s16 pos):
differs_from_disk(true), differs_from_disk(true),
usage_timer(0.0),
m_parent(parent), m_parent(parent),
m_pos(pos), m_pos(pos),
m_block_cache(NULL) m_block_cache(NULL)

@ -203,13 +203,6 @@ public:
// Basically, this should be changed to true in every setter method // Basically, this should be changed to true in every setter method
bool differs_from_disk; bool differs_from_disk;
// Counts seconds from last usage.
// Sector can be deleted from memory after some time of inactivity.
// NOTE: It has to be made very sure no other thread is accessing
// the sector and it doesn't remain in any cache when
// deleting it.
float usage_timer;
protected: protected:
// The pile of MapBlocks // The pile of MapBlocks

@ -1,119 +1,20 @@
#include "materials.h" #include "materials.h"
#include "mapnode.h"
#define MATERIAL_PROPERTIES_COUNT 256 // NOTE: DEPRECATED
// These correspond to the CONTENT_* constants DiggingPropertiesList * getDiggingPropertiesList(u8 content)
MaterialProperties g_material_properties[MATERIAL_PROPERTIES_COUNT];
bool g_material_properties_initialized = false;
void setStoneLikeDiggingProperties(u8 material, float toughness)
{ {
g_material_properties[material].setDiggingProperties("", return &content_features(content).digging_properties;
DiggingProperties(true, 15.0*toughness, 0));
g_material_properties[material].setDiggingProperties("WPick",
DiggingProperties(true, 1.3*toughness, 65535./30.*toughness));
g_material_properties[material].setDiggingProperties("STPick",
DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
g_material_properties[material].setDiggingProperties("SteelPick",
DiggingProperties(true, 0.50*toughness, 65535./333.*toughness));
/*g_material_properties[material].setDiggingProperties("MesePick",
DiggingProperties(true, 0.0*toughness, 65535./20.*toughness));*/
} }
void setDirtLikeDiggingProperties(u8 material, float toughness) DiggingProperties getDiggingProperties(u8 content, const std::string &tool)
{ {
g_material_properties[material].setDiggingProperties("", DiggingPropertiesList *mprop = getDiggingPropertiesList(content);
DiggingProperties(true, 0.75*toughness, 0));
g_material_properties[material].setDiggingProperties("WShovel",
DiggingProperties(true, 0.4*toughness, 65535./50.*toughness));
g_material_properties[material].setDiggingProperties("STShovel",
DiggingProperties(true, 0.2*toughness, 65535./150.*toughness));
g_material_properties[material].setDiggingProperties("SteelShovel",
DiggingProperties(true, 0.15*toughness, 65535./400.*toughness));
}
void setWoodLikeDiggingProperties(u8 material, float toughness)
{
g_material_properties[material].setDiggingProperties("",
DiggingProperties(true, 3.0*toughness, 0));
g_material_properties[material].setDiggingProperties("WAxe",
DiggingProperties(true, 1.5*toughness, 65535./30.*toughness));
g_material_properties[material].setDiggingProperties("STAxe",
DiggingProperties(true, 0.75*toughness, 65535./100.*toughness));
g_material_properties[material].setDiggingProperties("SteelAxe",
DiggingProperties(true, 0.5*toughness, 65535./333.*toughness));
}
void initializeMaterialProperties()
{
/*
Now, the g_material_properties array is already initialized
by the constructors to such that no digging is possible.
Add some digging properties to them.
*/
setStoneLikeDiggingProperties(CONTENT_STONE, 1.0);
setStoneLikeDiggingProperties(CONTENT_SANDSTONE, 1.0);
setStoneLikeDiggingProperties(CONTENT_BRICK, 3.0);
setStoneLikeDiggingProperties(CONTENT_MESE, 0.5);
setStoneLikeDiggingProperties(CONTENT_COALSTONE, 1.5);
setStoneLikeDiggingProperties(CONTENT_FURNACE, 3.0);
setStoneLikeDiggingProperties(CONTENT_COBBLE, 1.0);
setStoneLikeDiggingProperties(CONTENT_STEEL, 5.0);
setDirtLikeDiggingProperties(CONTENT_MUD, 1.0);
setDirtLikeDiggingProperties(CONTENT_GRASS, 1.0);
setDirtLikeDiggingProperties(CONTENT_GRASS_FOOTSTEPS, 1.0);
setDirtLikeDiggingProperties(CONTENT_SAND, 1.0);
setDirtLikeDiggingProperties(CONTENT_CLAY, 1.0);
setWoodLikeDiggingProperties(CONTENT_TREE, 1.0);
setWoodLikeDiggingProperties(CONTENT_LEAVES, 0.15);
setWoodLikeDiggingProperties(CONTENT_CACTUS, 0.75);
setWoodLikeDiggingProperties(CONTENT_PAPYRUS, 0.25);
setWoodLikeDiggingProperties(CONTENT_GLASS, 0.15);
setWoodLikeDiggingProperties(CONTENT_FENCE, 0.75);
setDirtLikeDiggingProperties(CONTENT_RAIL, 0.75);
setWoodLikeDiggingProperties(CONTENT_WOOD, 0.75);
setWoodLikeDiggingProperties(CONTENT_BOOKSHELF, 0.75);
setWoodLikeDiggingProperties(CONTENT_CHEST, 1.0);
g_material_properties[CONTENT_SIGN_WALL].setDiggingProperties("",
DiggingProperties(true, 0.5, 0));
g_material_properties[CONTENT_TORCH].setDiggingProperties("",
DiggingProperties(true, 0.0, 0));
/*
Add MesePick to everything
*/
for(u16 i=0; i<MATERIAL_PROPERTIES_COUNT; i++)
{
g_material_properties[i].setDiggingProperties("MesePick",
DiggingProperties(true, 0.0, 65535./1337));
}
g_material_properties_initialized = true;
}
MaterialProperties * getMaterialProperties(u8 material)
{
assert(g_material_properties_initialized);
return &g_material_properties[material];
}
DiggingProperties getDiggingProperties(u8 material, const std::string &tool)
{
MaterialProperties *mprop = getMaterialProperties(material);
if(mprop == NULL) if(mprop == NULL)
// Not diggable // Not diggable
return DiggingProperties(); return DiggingProperties();
return mprop->getDiggingProperties(tool); return mprop->get(tool);
} }

@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "inventory.h"
#include <string> #include <string>
struct DiggingProperties struct DiggingProperties
@ -49,20 +48,26 @@ struct DiggingProperties
u16 wear; u16 wear;
}; };
class MaterialProperties /*
This is a bad way of determining mining characteristics.
TODO: Get rid of this and set up some attributes like toughness,
fluffyness, and a funciton to calculate time and durability loss
(and sound? and whatever else) from them
*/
class DiggingPropertiesList
{ {
public: public:
MaterialProperties() DiggingPropertiesList()
{ {
} }
void setDiggingProperties(const std::string toolname, void set(const std::string toolname,
const DiggingProperties &prop) const DiggingProperties &prop)
{ {
m_digging_properties[toolname] = prop; m_digging_properties[toolname] = prop;
} }
DiggingProperties getDiggingProperties(const std::string toolname) DiggingProperties get(const std::string toolname)
{ {
core::map<std::string, DiggingProperties>::Node *n; core::map<std::string, DiggingProperties>::Node *n;
n = m_digging_properties.find(toolname); n = m_digging_properties.find(toolname);
@ -80,16 +85,17 @@ public:
return n->getValue(); return n->getValue();
} }
void clear()
{
m_digging_properties.clear();
}
private: private:
// toolname="": default properties (digging by hand) // toolname="": default properties (digging by hand)
// Key is toolname // Key is toolname
core::map<std::string, DiggingProperties> m_digging_properties; core::map<std::string, DiggingProperties> m_digging_properties;
}; };
void initializeMaterialProperties();
// Material correspond to the CONTENT_* constants
MaterialProperties * getMaterialProperties(u8 material);
// For getting the default properties, set tool="" // For getting the default properties, set tool=""
DiggingProperties getDiggingProperties(u8 material, const std::string &tool); DiggingProperties getDiggingProperties(u8 material, const std::string &tool);

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h" #include "exceptions.h"
#include "inventory.h" #include "inventory.h"
#include <sstream> #include <sstream>
#include "content_mapnode.h"
/* /*
NodeMetadata NodeMetadata
@ -96,268 +97,7 @@ void NodeMetadata::registerType(u16 id, Factory f)
} }
/* /*
SignNodeMetadata NodeMetadataList
*/
// Prototype
SignNodeMetadata proto_SignNodeMetadata("");
SignNodeMetadata::SignNodeMetadata(std::string text):
m_text(text)
{
NodeMetadata::registerType(typeId(), create);
}
u16 SignNodeMetadata::typeId() const
{
return CONTENT_SIGN_WALL;
}
NodeMetadata* SignNodeMetadata::create(std::istream &is)
{
std::string text = deSerializeString(is);
return new SignNodeMetadata(text);
}
NodeMetadata* SignNodeMetadata::clone()
{
return new SignNodeMetadata(m_text);
}
void SignNodeMetadata::serializeBody(std::ostream &os)
{
os<<serializeString(m_text);
}
std::string SignNodeMetadata::infoText()
{
return std::string("\"")+m_text+"\"";
}
/*
ChestNodeMetadata
*/
// Prototype
ChestNodeMetadata proto_ChestNodeMetadata;
ChestNodeMetadata::ChestNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);
m_inventory = new Inventory();
m_inventory->addList("0", 8*4);
}
ChestNodeMetadata::~ChestNodeMetadata()
{
delete m_inventory;
}
u16 ChestNodeMetadata::typeId() const
{
return CONTENT_CHEST;
}
NodeMetadata* ChestNodeMetadata::create(std::istream &is)
{
ChestNodeMetadata *d = new ChestNodeMetadata();
d->m_inventory->deSerialize(is);
return d;
}
NodeMetadata* ChestNodeMetadata::clone()
{
ChestNodeMetadata *d = new ChestNodeMetadata();
*d->m_inventory = *m_inventory;
return d;
}
void ChestNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
}
std::string ChestNodeMetadata::infoText()
{
return "Chest";
}
bool ChestNodeMetadata::nodeRemovalDisabled()
{
/*
Disable removal if chest contains something
*/
InventoryList *list = m_inventory->getList("0");
if(list == NULL)
return false;
if(list->getUsedSlots() == 0)
return false;
return true;
}
/*
FurnaceNodeMetadata
*/
// Prototype
FurnaceNodeMetadata proto_FurnaceNodeMetadata;
FurnaceNodeMetadata::FurnaceNodeMetadata()
{
NodeMetadata::registerType(typeId(), create);
m_inventory = new Inventory();
m_inventory->addList("fuel", 1);
m_inventory->addList("src", 1);
m_inventory->addList("dst", 4);
m_step_accumulator = 0;
m_fuel_totaltime = 0;
m_fuel_time = 0;
m_src_totaltime = 0;
m_src_time = 0;
}
FurnaceNodeMetadata::~FurnaceNodeMetadata()
{
delete m_inventory;
}
u16 FurnaceNodeMetadata::typeId() const
{
return CONTENT_FURNACE;
}
NodeMetadata* FurnaceNodeMetadata::clone()
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
*d->m_inventory = *m_inventory;
return d;
}
NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
{
FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
d->m_inventory->deSerialize(is);
int temp;
is>>temp;
d->m_fuel_totaltime = (float)temp/10;
is>>temp;
d->m_fuel_time = (float)temp/10;
return d;
}
void FurnaceNodeMetadata::serializeBody(std::ostream &os)
{
m_inventory->serialize(os);
os<<itos(m_fuel_totaltime*10)<<" ";
os<<itos(m_fuel_time*10)<<" ";
}
std::string FurnaceNodeMetadata::infoText()
{
//return "Furnace";
if(m_fuel_time >= m_fuel_totaltime)
{
InventoryList *src_list = m_inventory->getList("src");
assert(src_list);
InventoryItem *src_item = src_list->getItem(0);
if(src_item)
return "Furnace is out of fuel";
else
return "Furnace is inactive";
}
else
{
std::string s = "Furnace is active (";
s += itos(m_fuel_time/m_fuel_totaltime*100);
s += "%)";
return s;
}
}
void FurnaceNodeMetadata::inventoryModified()
{
dstream<<"Furnace inventory modification callback"<<std::endl;
}
bool FurnaceNodeMetadata::step(float dtime)
{
// Update at a fixed frequency
const float interval = 0.5;
m_step_accumulator += dtime;
if(m_step_accumulator < interval)
return false;
m_step_accumulator -= interval;
dtime = interval;
//dstream<<"Furnace step dtime="<<dtime<<std::endl;
InventoryList *dst_list = m_inventory->getList("dst");
assert(dst_list);
InventoryList *src_list = m_inventory->getList("src");
assert(src_list);
InventoryItem *src_item = src_list->getItem(0);
// Start only if there are free slots in dst, so that it can
// accomodate any result item
if(dst_list->getFreeSlots() > 0 && src_item && src_item->isCookable())
{
m_src_totaltime = 3;
}
else
{
m_src_time = 0;
m_src_totaltime = 0;
}
if(m_fuel_time < m_fuel_totaltime)
{
//dstream<<"Furnace is active"<<std::endl;
m_fuel_time += dtime;
m_src_time += dtime;
if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
&& src_item)
{
InventoryItem *cookresult = src_item->createCookResult();
dst_list->addItem(cookresult);
src_list->decrementMaterials(1);
m_src_time = 0;
m_src_totaltime = 0;
}
return true;
}
if(src_item == NULL || m_src_totaltime < 0.001)
{
return false;
}
bool changed = false;
//dstream<<"Furnace is out of fuel"<<std::endl;
InventoryList *fuel_list = m_inventory->getList("fuel");
assert(fuel_list);
InventoryItem *fuel_item = fuel_list->getItem(0);
if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
{
m_fuel_totaltime = 10;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
{
m_fuel_totaltime = 5;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
{
m_fuel_totaltime = 10;
m_fuel_time = 0;
fuel_list->decrementMaterials(1);
changed = true;
}
else
{
//dstream<<"No fuel found"<<std::endl;
}
return changed;
}
/*
NodeMetadatalist
*/ */
void NodeMetadataList::serialize(std::ostream &os) void NodeMetadataList::serialize(std::ostream &os)
@ -484,19 +224,6 @@ bool NodeMetadataList::step(float dtime)
bool changed = meta->step(dtime); bool changed = meta->step(dtime);
if(changed) if(changed)
something_changed = true; something_changed = true;
/*if(res.inventory_changed)
{
std::string inv_id;
inv_id += "nodemeta:";
inv_id += itos(p.X);
inv_id += ",";
inv_id += itos(p.Y);
inv_id += ",";
inv_id += itos(p.Z);
InventoryContext c;
c.current_player = NULL;
inv_mgr->inventoryModified(&c, inv_id);
}*/
} }
return something_changed; return something_changed;
} }

@ -62,6 +62,9 @@ public:
// A step in time. Returns true if metadata changed. // A step in time. Returns true if metadata changed.
virtual bool step(float dtime) {return false;} virtual bool step(float dtime) {return false;}
virtual bool nodeRemovalDisabled(){return false;} virtual bool nodeRemovalDisabled(){return false;}
// Used to make custom inventory menus.
// See format in guiInventoryMenu.cpp.
virtual std::string getInventoryDrawSpecString(){return "";}
protected: protected:
static void registerType(u16 id, Factory f); static void registerType(u16 id, Factory f);
@ -69,74 +72,10 @@ private:
static core::map<u16, Factory> m_types; static core::map<u16, Factory> m_types;
}; };
class SignNodeMetadata : public NodeMetadata
{
public:
SignNodeMetadata(std::string text);
//~SignNodeMetadata();
virtual u16 typeId() const;
static NodeMetadata* create(std::istream &is);
virtual NodeMetadata* clone();
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
std::string getText(){ return m_text; }
void setText(std::string t){ m_text = t; }
private:
std::string m_text;
};
class ChestNodeMetadata : public NodeMetadata
{
public:
ChestNodeMetadata();
~ChestNodeMetadata();
virtual u16 typeId() const;
static NodeMetadata* create(std::istream &is);
virtual NodeMetadata* clone();
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual bool nodeRemovalDisabled();
private:
Inventory *m_inventory;
};
class FurnaceNodeMetadata : public NodeMetadata
{
public:
FurnaceNodeMetadata();
~FurnaceNodeMetadata();
virtual u16 typeId() const;
virtual NodeMetadata* clone();
static NodeMetadata* create(std::istream &is);
virtual void serializeBody(std::ostream &os);
virtual std::string infoText();
virtual Inventory* getInventory() {return m_inventory;}
virtual void inventoryModified();
virtual bool step(float dtime);
private:
Inventory *m_inventory;
float m_step_accumulator;
float m_fuel_totaltime;
float m_fuel_time;
float m_src_totaltime;
float m_src_time;
};
/* /*
List of metadata of all the nodes of a block List of metadata of all the nodes of a block
*/ */
class InventoryManager;
class NodeMetadataList class NodeMetadataList
{ {
public: public:

@ -222,6 +222,49 @@ double noise3d_perlin_abs(double x, double y, double z, int seed,
return a; return a;
} }
// -1->0, 0->1, 1->0
double contour(double v)
{
v = fabs(v);
if(v >= 1.0)
return 0.0;
return (1.0-v);
}
double noise3d_param(const NoiseParams &param, double x, double y, double z)
{
double s = param.pos_scale;
x /= s;
y /= s;
z /= s;
if(param.type == NOISE_PERLIN)
{
return param.noise_scale*noise3d_perlin(x,y,z, param.seed,
param.octaves,
param.persistence);
}
else if(param.type == NOISE_PERLIN_ABS)
{
return param.noise_scale*noise3d_perlin_abs(x,y,z, param.seed,
param.octaves,
param.persistence);
}
else if(param.type == NOISE_PERLIN_CONTOUR)
{
return contour(param.noise_scale*noise3d_perlin(x,y,z,
param.seed, param.octaves,
param.persistence));
}
else if(param.type == NOISE_PERLIN_CONTOUR_FLIP_YZ)
{
return contour(param.noise_scale*noise3d_perlin(x,z,y,
param.seed, param.octaves,
param.persistence));
}
else assert(0);
}
/* /*
NoiseBuffer NoiseBuffer
*/ */
@ -246,8 +289,7 @@ void NoiseBuffer::clear()
m_size_z = 0; m_size_z = 0;
} }
void NoiseBuffer::create(int seed, int octaves, double persistence, void NoiseBuffer::create(const NoiseParams &param,
double pos_scale,
double first_x, double first_y, double first_z, double first_x, double first_y, double first_z,
double last_x, double last_y, double last_z, double last_x, double last_y, double last_z,
double samplelength_x, double samplelength_y, double samplelength_z) double samplelength_x, double samplelength_y, double samplelength_z)
@ -265,22 +307,54 @@ void NoiseBuffer::create(int seed, int octaves, double persistence,
m_size_y = (last_y - m_start_y)/samplelength_y + 2; m_size_y = (last_y - m_start_y)/samplelength_y + 2;
m_size_z = (last_z - m_start_z)/samplelength_z + 2; m_size_z = (last_z - m_start_z)/samplelength_z + 2;
/*dstream<<"m_size_x="<<m_size_x<<", m_size_y="<<m_size_y
<<", m_size_z="<<m_size_z<<std::endl;*/
m_data = new double[m_size_x*m_size_y*m_size_z]; m_data = new double[m_size_x*m_size_y*m_size_z];
for(int x=0; x<m_size_x; x++) for(int x=0; x<m_size_x; x++)
for(int y=0; y<m_size_y; y++) for(int y=0; y<m_size_y; y++)
for(int z=0; z<m_size_z; z++) for(int z=0; z<m_size_z; z++)
{ {
double xd = (m_start_x + (double)x*m_samplelength_x)/pos_scale; double xd = (m_start_x + (double)x*m_samplelength_x);
double yd = (m_start_y + (double)y*m_samplelength_y)/pos_scale; double yd = (m_start_y + (double)y*m_samplelength_y);
double zd = (m_start_z + (double)z*m_samplelength_z)/pos_scale; double zd = (m_start_z + (double)z*m_samplelength_z);
intSet(x,y,z, noise3d_perlin(xd,yd,zd,seed,octaves,persistence)); double a = noise3d_param(param, xd,yd,zd);
intSet(x,y,z, a);
} }
} }
void NoiseBuffer::multiply(const NoiseParams &param)
{
assert(m_data != NULL);
for(int x=0; x<m_size_x; x++)
for(int y=0; y<m_size_y; y++)
for(int z=0; z<m_size_z; z++)
{
double xd = (m_start_x + (double)x*m_samplelength_x);
double yd = (m_start_y + (double)y*m_samplelength_y);
double zd = (m_start_z + (double)z*m_samplelength_z);
double a = noise3d_param(param, xd,yd,zd);
intMultiply(x,y,z, a);
}
}
// Deprecated
void NoiseBuffer::create(int seed, int octaves, double persistence,
bool abs,
double first_x, double first_y, double first_z,
double last_x, double last_y, double last_z,
double samplelength_x, double samplelength_y, double samplelength_z)
{
NoiseParams param;
param.type = abs ? NOISE_PERLIN_ABS : NOISE_PERLIN;
param.seed = seed;
param.octaves = octaves;
param.persistence = persistence;
create(param, first_x, first_y, first_z,
last_x, last_y, last_z,
samplelength_x, samplelength_y, samplelength_z);
}
void NoiseBuffer::intSet(int x, int y, int z, double d) void NoiseBuffer::intSet(int x, int y, int z, double d)
{ {
int i = m_size_x*m_size_y*z + m_size_x*y + x; int i = m_size_x*m_size_y*z + m_size_x*y + x;
@ -289,6 +363,14 @@ void NoiseBuffer::intSet(int x, int y, int z, double d)
m_data[i] = d; m_data[i] = d;
} }
void NoiseBuffer::intMultiply(int x, int y, int z, double d)
{
int i = m_size_x*m_size_y*z + m_size_x*y + x;
assert(i >= 0);
assert(i < m_size_x*m_size_y*m_size_z);
m_data[i] = m_data[i] * d;
}
double NoiseBuffer::intGet(int x, int y, int z) double NoiseBuffer::intGet(int x, int y, int z)
{ {
int i = m_size_x*m_size_y*z + m_size_x*y + x; int i = m_size_x*m_size_y*z + m_size_x*y + x;
@ -326,3 +408,14 @@ double NoiseBuffer::get(double x, double y, double z)
return triLinearInterpolation(v000,v100,v010,v110,v001,v101,v011,v111,xl,yl,zl); return triLinearInterpolation(v000,v100,v010,v110,v001,v101,v011,v111,xl,yl,zl);
} }
/*bool NoiseBuffer::contains(double x, double y, double z)
{
x -= m_start_x;
y -= m_start_y;
z -= m_start_z;
x /= m_samplelength_x;
y /= m_samplelength_y;
z /= m_samplelength_z;
if(x <= 0.0 || x >= m_size_x)
}*/

@ -20,6 +20,45 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef NOISE_HEADER #ifndef NOISE_HEADER
#define NOISE_HEADER #define NOISE_HEADER
#include "debug.h"
class PseudoRandom
{
public:
PseudoRandom(): m_next(0)
{
}
PseudoRandom(int seed): m_next(seed)
{
}
void seed(int seed)
{
m_next = seed;
}
// Returns 0...32767
int next()
{
m_next = m_next * 1103515245 + 12345;
return((unsigned)(m_next/65536) % 32768);
}
int range(int min, int max)
{
if(max-min > 32768/10)
{
//dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl;
assert(0);
}
if(min > max)
{
assert(0);
return max;
}
return (next()%(max-min+1))+min;
}
private:
int m_next;
};
double easeCurve(double t); double easeCurve(double t);
// Return value: -1 ... 1 // Return value: -1 ... 1
@ -41,6 +80,38 @@ double noise3d_perlin(double x, double y, double z, int seed,
double noise3d_perlin_abs(double x, double y, double z, int seed, double noise3d_perlin_abs(double x, double y, double z, int seed,
int octaves, double persistence); int octaves, double persistence);
enum NoiseType
{
NOISE_PERLIN,
NOISE_PERLIN_ABS,
NOISE_PERLIN_CONTOUR,
NOISE_PERLIN_CONTOUR_FLIP_YZ
};
struct NoiseParams
{
NoiseType type;
int seed;
int octaves;
double persistence;
double pos_scale;
double noise_scale; // Useful for contour noises
NoiseParams(NoiseType type_=NOISE_PERLIN, int seed_=0,
int octaves_=3, double persistence_=0.5,
double pos_scale_=100.0, double noise_scale_=1.0):
type(type_),
seed(seed_),
octaves(octaves_),
persistence(persistence_),
pos_scale(pos_scale_),
noise_scale(noise_scale_)
{
}
};
double noise3d_param(const NoiseParams &param, double x, double y, double z);
class NoiseBuffer class NoiseBuffer
{ {
public: public:
@ -48,15 +119,23 @@ public:
~NoiseBuffer(); ~NoiseBuffer();
void clear(); void clear();
void create(const NoiseParams &param,
double first_x, double first_y, double first_z,
double last_x, double last_y, double last_z,
double samplelength_x, double samplelength_y, double samplelength_z);
void multiply(const NoiseParams &param);
// Deprecated
void create(int seed, int octaves, double persistence, void create(int seed, int octaves, double persistence,
double pos_scale, bool abs,
double first_x, double first_y, double first_z, double first_x, double first_y, double first_z,
double last_x, double last_y, double last_z, double last_x, double last_y, double last_z,
double samplelength_x, double samplelength_y, double samplelength_z); double samplelength_x, double samplelength_y, double samplelength_z);
void intSet(int x, int y, int z, double d); void intSet(int x, int y, int z, double d);
void intMultiply(int x, int y, int z, double d);
double intGet(int x, int y, int z); double intGet(int x, int y, int z);
double get(double x, double y, double z); double get(double x, double y, double z);
//bool contains(double x, double y, double z);
private: private:
double *m_data; double *m_data;

@ -23,58 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h" #include "constants.h"
#include "utility.h" #include "utility.h"
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::wstring privsToString(u64 privs)
{
std::wostringstream os(std::ios_base::binary);
if(privs & PRIV_BUILD)
os<<L"build,";
if(privs & PRIV_TELEPORT)
os<<L"teleport,";
if(privs & PRIV_SETTIME)
os<<L"settime,";
if(privs & PRIV_PRIVS)
os<<L"privs,";
if(privs & PRIV_SHOUT)
os<<L"shout,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::wstring tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::wstring str)
{
u64 privs=0;
std::vector<std::wstring> pr;
pr=str_split(str, ',');
for(std::vector<std::wstring>::iterator i = pr.begin();
i != pr.end(); ++i)
{
if(*i == L"build")
privs |= PRIV_BUILD;
else if(*i == L"teleport")
privs |= PRIV_TELEPORT;
else if(*i == L"settime")
privs |= PRIV_SETTIME;
else if(*i == L"privs")
privs |= PRIV_PRIVS;
else if(*i == L"shout")
privs |= PRIV_SHOUT;
else
return PRIV_INVALID;
}
return privs;
}
Player::Player(): Player::Player():
touching_ground(false), touching_ground(false),
@ -83,7 +31,6 @@ Player::Player():
swimming_up(false), swimming_up(false),
craftresult_is_preview(true), craftresult_is_preview(true),
hp(20), hp(20),
privs(PRIV_DEFAULT),
peer_id(PEER_ID_INEXISTENT), peer_id(PEER_ID_INEXISTENT),
m_pitch(0), m_pitch(0),
m_yaw(0), m_yaw(0),
@ -91,7 +38,6 @@ Player::Player():
m_position(0,0,0) m_position(0,0,0)
{ {
updateName("<not set>"); updateName("<not set>");
updatePassword("");
resetInventory(); resetInventory();
} }
@ -150,13 +96,12 @@ void Player::serialize(std::ostream &os)
Settings args; Settings args;
args.setS32("version", 1); args.setS32("version", 1);
args.set("name", m_name); args.set("name", m_name);
args.set("password", m_password); //args.set("password", m_password);
args.setFloat("pitch", m_pitch); args.setFloat("pitch", m_pitch);
args.setFloat("yaw", m_yaw); args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position); args.setV3F("position", m_position);
args.setBool("craftresult_is_preview", craftresult_is_preview); args.setBool("craftresult_is_preview", craftresult_is_preview);
args.setS32("hp", hp); args.setS32("hp", hp);
args.setU64("privs", privs);
args.writeLines(os); args.writeLines(os);
@ -185,10 +130,10 @@ void Player::deSerialize(std::istream &is)
//args.getS32("version"); //args.getS32("version");
std::string name = args.get("name"); std::string name = args.get("name");
updateName(name.c_str()); updateName(name.c_str());
std::string password = ""; /*std::string password = "";
if(args.exists("password")) if(args.exists("password"))
password = args.get("password"); password = args.get("password");
updatePassword(password.c_str()); updatePassword(password.c_str());*/
m_pitch = args.getFloat("pitch"); m_pitch = args.getFloat("pitch");
m_yaw = args.getFloat("yaw"); m_yaw = args.getFloat("yaw");
m_position = args.getV3F("position"); m_position = args.getV3F("position");
@ -202,7 +147,7 @@ void Player::deSerialize(std::istream &is)
}catch(SettingNotFoundException &e){ }catch(SettingNotFoundException &e){
hp = 20; hp = 20;
} }
try{ /*try{
std::string sprivs = args.get("privs"); std::string sprivs = args.get("privs");
if(sprivs == "all") if(sprivs == "all")
{ {
@ -215,7 +160,7 @@ void Player::deSerialize(std::istream &is)
} }
}catch(SettingNotFoundException &e){ }catch(SettingNotFoundException &e){
privs = PRIV_DEFAULT; privs = PRIV_DEFAULT;
} }*/
inventory.deSerialize(is); inventory.deSerialize(is);
} }

@ -25,39 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h" #include "collision.h"
#define PLAYERNAME_SIZE 20 #define PLAYERNAME_SIZE 20
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
// base64-encoded SHA-1.
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.," #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_BUILD = 1; // Can build - i.e. modify the world
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_BUILD|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::wstring privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::wstring str);
class Map; class Map;
@ -128,16 +97,6 @@ public:
return m_name; return m_name;
} }
virtual void updatePassword(const char *password)
{
snprintf(m_password, PASSWORD_SIZE, "%s", password);
}
const char * getPassword()
{
return m_password;
}
virtual bool isLocal() const = 0; virtual bool isLocal() const = 0;
virtual void updateLight(u8 light_at_pos) {}; virtual void updateLight(u8 light_at_pos) {};
@ -167,14 +126,10 @@ public:
u16 hp; u16 hp;
// Player's privileges - a bitmaps of PRIV_xxxx.
u64 privs;
u16 peer_id; u16 peer_id;
protected: protected:
char m_name[PLAYERNAME_SIZE]; char m_name[PLAYERNAME_SIZE];
char m_password[PASSWORD_SIZE];
f32 m_pitch; f32 m_pitch;
f32 m_yaw; f32 m_yaw;
v3f m_speed; v3f m_speed;

@ -27,6 +27,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
#ifdef __APPLE__
#include "CoreFoundation/CoreFoundation.h"
#endif
namespace porting namespace porting
{ {
@ -215,8 +219,25 @@ void initializePaths()
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <unistd.h> #include <unistd.h>
// Code based on
// http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
CFBundleRef main_bundle = CFBundleGetMainBundle();
CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
char path[PATH_MAX];
if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
{
dstream<<"Bundle resource path: "<<path<<std::endl;
//chdir(path);
path_data = std::string(path) + "/data";
}
else
{
// error!
dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
}
CFRelease(resources_url);
path_userdata = std::string(getenv("HOME")) + "/Library/Application Support/" + APPNAME; path_userdata = std::string(getenv("HOME")) + "/Library/Application Support/" + APPNAME;
path_data = std::string("minetest-mac.app/Contents/Resources/data/");
#endif #endif

131
src/profiler.h Normal file

@ -0,0 +1,131 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 PROFILER_HEADER
#define PROFILER_HEADER
#include "common_irrlicht.h"
#include <string>
#include "utility.h"
#include <jmutex.h>
#include <jmutexautolock.h>
/*
Time profiler
*/
class Profiler
{
public:
Profiler()
{
m_mutex.Init();
}
void add(const std::string &name, u32 duration)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, u32>::Node *n = m_data.find(name);
if(n == NULL)
{
m_data[name] = duration;
}
else
{
n->setValue(n->getValue()+duration);
}
}
void clear()
{
JMutexAutoLock lock(m_mutex);
for(core::map<std::string, u32>::Iterator
i = m_data.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->setValue(0);
}
}
void print(std::ostream &o)
{
JMutexAutoLock lock(m_mutex);
for(core::map<std::string, u32>::Iterator
i = m_data.getIterator();
i.atEnd() == false; i++)
{
std::string name = i.getNode()->getKey();
o<<name<<": ";
s32 clampsize = 40;
s32 space = clampsize-name.size();
for(s32 j=0; j<space; j++)
{
if(j%2 == 0 && j < space - 1)
o<<"-";
else
o<<" ";
}
o<<i.getNode()->getValue();
o<<std::endl;
}
}
private:
JMutex m_mutex;
core::map<std::string, u32> m_data;
};
class ScopeProfiler
{
public:
ScopeProfiler(Profiler *profiler, const std::string &name):
m_profiler(profiler),
m_name(name),
m_timer(NULL)
{
if(m_profiler)
m_timer = new TimeTaker(m_name.c_str());
}
// name is copied
ScopeProfiler(Profiler *profiler, const char *name):
m_profiler(profiler),
m_name(name),
m_timer(NULL)
{
if(m_profiler)
m_timer = new TimeTaker(m_name.c_str());
}
~ScopeProfiler()
{
if(m_timer)
{
u32 duration = m_timer->stop(true);
if(m_profiler)
m_profiler->add(m_name, duration);
delete m_timer;
}
}
private:
Profiler *m_profiler;
std::string m_name;
TimeTaker *m_timer;
};
#endif

@ -128,7 +128,7 @@ void decompressZlib(std::istream &is, std::ostream &os)
ret = inflateInit(&z); ret = inflateInit(&z);
if(ret != Z_OK) if(ret != Z_OK)
throw SerializationError("compressZlib: inflateInit failed"); throw SerializationError("dcompressZlib: inflateInit failed");
z.avail_in = 0; z.avail_in = 0;
@ -162,7 +162,7 @@ void decompressZlib(std::istream &is, std::ostream &os)
|| status == Z_MEM_ERROR) || status == Z_MEM_ERROR)
{ {
zerr(status); zerr(status);
throw SerializationError("compressZlib: inflate failed"); throw SerializationError("decompressZlib: inflate failed");
} }
int count = bufsize - z.avail_out; int count = bufsize - z.avail_out;
//dstream<<"count="<<count<<std::endl; //dstream<<"count="<<count<<std::endl;
@ -182,7 +182,7 @@ void decompressZlib(std::istream &is, std::ostream &os)
{ {
dstream<<"unget #"<<i<<" failed"<<std::endl; dstream<<"unget #"<<i<<" failed"<<std::endl;
dstream<<"fail="<<is.fail()<<" bad="<<is.bad()<<std::endl; dstream<<"fail="<<is.fail()<<" bad="<<is.bad()<<std::endl;
throw SerializationError("compressZlib: unget failed"); throw SerializationError("decompressZlib: unget failed");
} }
} }

@ -53,11 +53,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
15: StaticObjects 15: StaticObjects
16: larger maximum size of node metadata, and compression 16: larger maximum size of node metadata, and compression
17: MapBlocks contain timestamp 17: MapBlocks contain timestamp
18: sqlite/new generator/whatever
*/ */
// This represents an uninitialized or invalid format // This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255 #define SER_FMT_VER_INVALID 255
// Highest supported serialization version // Highest supported serialization version
#define SER_FMT_VER_HIGHEST 17 #define SER_FMT_VER_HIGHEST 18
// Lowest supported serialization version // Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0 #define SER_FMT_VER_LOWEST 0

File diff suppressed because it is too large Load Diff

@ -17,10 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
/*
(c) 2010 Perttu Ahola <celeron55@gmail.com>
*/
#ifndef SERVER_HEADER #ifndef SERVER_HEADER
#define SERVER_HEADER #define SERVER_HEADER
@ -32,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "map.h" #include "map.h"
#include "inventory.h" #include "inventory.h"
#include "auth.h"
/* /*
Some random functions Some random functions
@ -424,6 +421,28 @@ public:
// Envlock and conlock should be locked when calling this // Envlock and conlock should be locked when calling this
void SendMovePlayer(Player *player); void SendMovePlayer(Player *player);
u64 getPlayerAuthPrivs(const std::string &name)
{
try{
return m_authmanager.getPrivs(name);
}
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
return 0;
}
}
void setPlayerAuthPrivs(const std::string &name, u64 privs)
{
try{
return m_authmanager.setPrivs(name, privs);
}
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
}
}
private: private:
@ -438,7 +457,8 @@ private:
*/ */
static void SendHP(con::Connection &con, u16 peer_id, u8 hp); static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id); static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
/* /*
Non-static send methods Non-static send methods
@ -456,15 +476,17 @@ private:
Additionally, if far_players!=NULL, players further away than Additionally, if far_players!=NULL, players further away than
far_d_nodes are ignored and their peer_ids are added to far_players far_d_nodes are ignored and their peer_ids are added to far_players
*/ */
// Envlock and conlock should be locked when calling these
void sendRemoveNode(v3s16 p, u16 ignore_id=0, void sendRemoveNode(v3s16 p, u16 ignore_id=0,
core::list<u16> *far_players=NULL, float far_d_nodes=100); core::list<u16> *far_players=NULL, float far_d_nodes=100);
void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0, void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0,
core::list<u16> *far_players=NULL, float far_d_nodes=100); core::list<u16> *far_players=NULL, float far_d_nodes=100);
void setBlockNotSent(v3s16 p);
// Environment and Connection must be locked when called // Environment and Connection must be locked when called
void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver); void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver);
// Sends blocks to clients // Sends blocks to clients (locks env and con on its own)
void SendBlocks(float dtime); void SendBlocks(float dtime);
/* /*
@ -476,6 +498,15 @@ private:
// When called, connection mutex should be locked // When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id); RemoteClient* getClient(u16 peer_id);
// When called, environment mutex should be locked
std::string getPlayerName(u16 peer_id)
{
Player *player = m_env.getPlayer(peer_id);
if(player == NULL)
return "[id="+itos(peer_id);
return player->getName();
}
/* /*
Get a player from memory or creates one. Get a player from memory or creates one.
If player is already connected, return NULL If player is already connected, return NULL
@ -491,6 +522,8 @@ private:
void handlePeerChange(PeerChange &c); void handlePeerChange(PeerChange &c);
void handlePeerChanges(); void handlePeerChanges();
u64 getPlayerPrivs(Player *player);
/* /*
Variables Variables
*/ */
@ -515,6 +548,9 @@ private:
// Connected clients (behind the con mutex) // Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients; core::map<u16, RemoteClient*> m_clients;
// User authentication
AuthManager m_authmanager;
/* /*
Threads Threads
*/ */
@ -598,6 +634,8 @@ private:
*/ */
u16 m_ignore_map_edit_events_peer_id; u16 m_ignore_map_edit_events_peer_id;
Profiler *m_profiler;
friend class EmergeThread; friend class EmergeThread;
friend class RemoteClient; friend class RemoteClient;
}; };

@ -1,24 +1,21 @@
/* /*
Minetest-c55 Part of Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com> Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
This program is free software; you can redistribute it and/or modify Permission to use, copy, modify, and distribute this software for any
it under the terms of the GNU General Public License as published by purpose with or without fee is hereby granted, provided that the above
the Free Software Foundation; either version 2 of the License, or copyright notice and this permission notice appear in all copies.
(at your option) any later version.
This program is distributed in the hope that it will be useful, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
but WITHOUT ANY WARRANTY; without even the implied warranty of WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
GNU General Public License for more details. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
You should have received a copy of the GNU General Public License along ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
with this program; if not, write to the Free Software Foundation, Inc., OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "servercommand.h" #include "servercommand.h"
#include "utility.h" #include "utility.h"
@ -35,7 +32,8 @@ void cmd_privs(std::wostringstream &os,
{ {
// Show our own real privs, without any adjustments // Show our own real privs, without any adjustments
// made for admin status // made for admin status
os<<L"-!- " + privsToString(ctx->player->privs); os<<L"-!- " + narrow_to_wide(privsToString(
ctx->server->getPlayerAuthPrivs(ctx->player->getName())));
return; return;
} }
@ -52,7 +50,7 @@ void cmd_privs(std::wostringstream &os,
return; return;
} }
os<<L"-!- " + privsToString(tp->privs); os<<L"-!- " + narrow_to_wide(privsToString(ctx->server->getPlayerAuthPrivs(tp->getName())));
} }
void cmd_grantrevoke(std::wostringstream &os, void cmd_grantrevoke(std::wostringstream &os,
@ -70,7 +68,7 @@ void cmd_grantrevoke(std::wostringstream &os,
return; return;
} }
u64 newprivs = stringToPrivs(ctx->parms[2]); u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
if(newprivs == PRIV_INVALID) if(newprivs == PRIV_INVALID)
{ {
os<<L"-!- Invalid privileges specified"; os<<L"-!- Invalid privileges specified";
@ -84,13 +82,18 @@ void cmd_grantrevoke(std::wostringstream &os,
return; return;
} }
std::string playername = wide_to_narrow(ctx->parms[1]);
u64 privs = ctx->server->getPlayerAuthPrivs(playername);
if(ctx->parms[0] == L"grant") if(ctx->parms[0] == L"grant")
tp->privs |= newprivs; privs |= newprivs;
else else
tp->privs &= ~newprivs; privs &= ~newprivs;
ctx->server->setPlayerAuthPrivs(playername, privs);
os<<L"-!- Privileges change to "; os<<L"-!- Privileges change to ";
os<<privsToString(tp->privs); os<<narrow_to_wide(privsToString(privs));
} }
void cmd_time(std::wostringstream &os, void cmd_time(std::wostringstream &os,

@ -1,21 +1,19 @@
/* /*
Minetest-c55 Part of Minetest-c55
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> Copyright (C) 2010-11 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com> Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.com>
This program is free software; you can redistribute it and/or modify Permission to use, copy, modify, and distribute this software for any
it under the terms of the GNU General Public License as published by purpose with or without fee is hereby granted, provided that the above
the Free Software Foundation; either version 2 of the License, or copyright notice and this permission notice appear in all copies.
(at your option) any later version.
This program is distributed in the hope that it will be useful, THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
but WITHOUT ANY WARRANTY; without even the implied warranty of WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
GNU General Public License for more details. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
You should have received a copy of the GNU General Public License along ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
with this program; if not, write to the Free Software Foundation, Inc., OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef SERVERCOMMAND_HEADER #ifndef SERVERCOMMAND_HEADER

@ -69,6 +69,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "materials.h" #include "materials.h"
#include "config.h" #include "config.h"
#include "mineral.h" #include "mineral.h"
#include "filesys.h"
/* /*
Settings. Settings.
@ -79,6 +80,9 @@ Settings g_settings;
extern void set_default_settings(); extern void set_default_settings();
// Global profiler
Profiler g_profiler;
// A dummy thing // A dummy thing
ITextureSource *g_texturesource = NULL; ITextureSource *g_texturesource = NULL;
@ -112,6 +116,15 @@ u32 getTimeMs()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
/*
Initialization
*/
// Set locale. This is for forcing '.' as the decimal point.
std::locale::global(std::locale("C"));
// This enables printing all characters in bitmap font
setlocale(LC_CTYPE, "en_US");
/* /*
Low-level initialization Low-level initialization
*/ */
@ -121,20 +134,31 @@ int main(int argc, char *argv[])
disable_stderr = true; disable_stderr = true;
#endif #endif
porting::signal_handler_init();
bool &kill = *porting::signal_handler_killstatus();
// Initialize porting::path_data and porting::path_userdata
porting::initializePaths();
// Create user data directory
fs::CreateDir(porting::path_userdata);
// Initialize debug streams // Initialize debug streams
debugstreams_init(disable_stderr, DEBUGFILE); #ifdef RUN_IN_PLACE
std::string debugfile = DEBUGFILE;
#else
std::string debugfile = porting::path_userdata+"/"+DEBUGFILE;
#endif
debugstreams_init(disable_stderr, debugfile.c_str());
// Initialize debug stacks // Initialize debug stacks
debug_stacks_init(); debug_stacks_init();
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
porting::signal_handler_init(); // Init material properties table
bool &kill = *porting::signal_handler_killstatus(); //initializeMaterialProperties();
porting::initializePaths();
initializeMaterialProperties();
// Debug handler
BEGIN_DEBUG_EXCEPTION_HANDLER BEGIN_DEBUG_EXCEPTION_HANDLER
// Print startup message // Print startup message
@ -199,19 +223,10 @@ int main(int argc, char *argv[])
// Initialize default settings // Initialize default settings
set_default_settings(); set_default_settings();
// Set locale. This is for forcing '.' as the decimal point.
std::locale::global(std::locale("C"));
// This enables printing all characters in bitmap font
setlocale(LC_CTYPE, "en_US");
// Initialize sockets // Initialize sockets
sockets_init(); sockets_init();
atexit(sockets_cleanup); atexit(sockets_cleanup);
/*
Initialization
*/
/* /*
Read config file Read config file
*/ */
@ -301,7 +316,7 @@ int main(int argc, char *argv[])
} }
// Figure out path to map // Figure out path to map
std::string map_dir = porting::path_userdata+"/map"; std::string map_dir = porting::path_userdata+"/world";
if(cmd_args.exists("map-dir")) if(cmd_args.exists("map-dir"))
map_dir = cmd_args.get("map-dir"); map_dir = cmd_args.get("map-dir");
else if(g_settings.exists("map-dir")) else if(g_settings.exists("map-dir"))

16
src/sqlite/CMakeLists.txt Normal file

@ -0,0 +1,16 @@
if( UNIX )
set(sqlite3_SRCS sqlite3.c)
set(sqlite3_platform_LIBS "")
else( UNIX )
set(sqlite3_SRCS sqlite3.c)
set(sqlite3_platform_LIBS "")
endif( UNIX )
add_library(sqlite3 ${sqlite3_SRCS})
target_link_libraries(
sqlite3
${sqlite3_platform_LIBS}
)

125968
src/sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

6464
src/sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h" #include "voxel.h"
#include <sstream> #include <sstream>
#include "porting.h" #include "porting.h"
#include "content_mapnode.h"
/* /*
Asserts that the exception occurs Asserts that the exception occurs
@ -424,16 +425,27 @@ struct TestMapBlock
assert(b.getChangedFlag() == false); assert(b.getChangedFlag() == false);
// All nodes should have been set to // All nodes should have been set to
// .d=CONTENT_AIR and .getLight() = 0 // .d=CONTENT_IGNORE and .getLight() = 0
for(u16 z=0; z<MAP_BLOCKSIZE; z++) for(u16 z=0; z<MAP_BLOCKSIZE; z++)
for(u16 y=0; y<MAP_BLOCKSIZE; y++) for(u16 y=0; y<MAP_BLOCKSIZE; y++)
for(u16 x=0; x<MAP_BLOCKSIZE; x++) for(u16 x=0; x<MAP_BLOCKSIZE; x++)
{ {
assert(b.getNode(v3s16(x,y,z)).d == CONTENT_AIR); //assert(b.getNode(v3s16(x,y,z)).d == CONTENT_AIR);
assert(b.getNode(v3s16(x,y,z)).d == CONTENT_IGNORE);
assert(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0); assert(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0);
assert(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0); assert(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0);
} }
{
MapNode n(CONTENT_AIR);
for(u16 z=0; z<MAP_BLOCKSIZE; z++)
for(u16 y=0; y<MAP_BLOCKSIZE; y++)
for(u16 x=0; x<MAP_BLOCKSIZE; x++)
{
b.setNode(v3s16(x,y,z), n);
}
}
/* /*
Parent fetch functions Parent fetch functions
*/ */

@ -506,22 +506,17 @@ void TextureSource::buildMainAtlas()
sourcelist.push_back("stone.png"); sourcelist.push_back("stone.png");
sourcelist.push_back("mud.png"); sourcelist.push_back("mud.png");
sourcelist.push_back("sand.png"); sourcelist.push_back("sand.png");
sourcelist.push_back("sandstone.png");
sourcelist.push_back("clay.png");
sourcelist.push_back("brick.png");
sourcelist.push_back("grass.png"); sourcelist.push_back("grass.png");
sourcelist.push_back("grass_footsteps.png"); sourcelist.push_back("grass_footsteps.png");
sourcelist.push_back("tree.png"); sourcelist.push_back("tree.png");
sourcelist.push_back("tree_top.png"); sourcelist.push_back("tree_top.png");
sourcelist.push_back("water.png"); sourcelist.push_back("water.png");
sourcelist.push_back("leaves.png"); sourcelist.push_back("leaves.png");
sourcelist.push_back("cactus_side.png");
sourcelist.push_back("cactus_top.png");
sourcelist.push_back("papyrus.png");
sourcelist.push_back("bookshelf.png");
sourcelist.push_back("glass.png"); sourcelist.push_back("glass.png");
sourcelist.push_back("mud.png^grass_side.png"); sourcelist.push_back("mud.png^grass_side.png");
sourcelist.push_back("cobble.png"); sourcelist.push_back("cobble.png");
sourcelist.push_back("mossycobble.png");
sourcelist.push_back("gravel.png");
sourcelist.push_back("stone.png^mineral_coal.png"); sourcelist.push_back("stone.png^mineral_coal.png");
sourcelist.push_back("stone.png^mineral_iron.png"); sourcelist.push_back("stone.png^mineral_iron.png");
@ -1092,7 +1087,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
imagename_right, device); imagename_right, device);
assert(img_top && img_left && img_right); assert(img_top && img_left && img_right);
// FIXME: Create textures from left and right images // TODO: Create textures from images
video::ITexture *texture_top = driver->addTexture( video::ITexture *texture_top = driver->addTexture(
(imagename_top + "__temp__").c_str(), img_top); (imagename_top + "__temp__").c_str(), img_top);
assert(texture_top); assert(texture_top);

@ -230,9 +230,9 @@ std::string translatePassword(std::string playername, std::wstring password)
return ""; return "";
std::string slt = playername + wide_to_narrow(password); std::string slt = playername + wide_to_narrow(password);
SHA1 *sha1 = new SHA1(); SHA1 sha1;
sha1->addBytes(slt.c_str(), slt.length()); sha1.addBytes(slt.c_str(), slt.length());
unsigned char *digest = sha1->getDigest(); unsigned char *digest = sha1.getDigest();
std::string pwd = base64_encode(digest, 20); std::string pwd = base64_encode(digest, 20);
free(digest); free(digest);
return pwd; return pwd;

@ -244,6 +244,9 @@ inline f32 readF1000(std::istream &is)
{ {
char buf[2]; char buf[2];
is.read(buf, 2); is.read(buf, 2);
// TODO: verify if this gets rid of the valgrind warning
//if(is.gcount() != 2)
// return 0;
return readF1000((u8*)buf); return readF1000((u8*)buf);
} }
@ -1738,6 +1741,11 @@ void mysrand(unsigned seed);
inline int myrand_range(int min, int max) inline int myrand_range(int min, int max)
{ {
if(max-min > MYRAND_MAX)
{
dstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
assert(0);
}
if(min > max) if(min > max)
{ {
assert(0); assert(0);
@ -1984,17 +1992,23 @@ inline std::string serializeString(const std::string &plain)
return s; return s;
} }
/*// Reads a string with the length as the first two bytes // Creates a string with the length as the first two bytes from wide string
inline std::string deSerializeString(const std::string encoded) inline std::string serializeWideString(const std::wstring &plain)
{ {
u16 s_size = readU16((u8*)&encoded.c_str()[0]); //assert(plain.size() <= 65535);
if(s_size > encoded.length() - 2) if(plain.size() > 65535)
return ""; throw SerializationError("String too long for serializeString");
char buf[2];
writeU16((u8*)buf, plain.size());
std::string s; std::string s;
s.reserve(s_size); s.append(buf, 2);
s.append(&encoded.c_str()[2], s_size); for(u32 i=0; i<plain.size(); i++)
{
writeU16((u8*)buf, plain[i]);
s.append(buf, 2);
}
return s; return s;
}*/ }
// Reads a string with the length as the first two bytes // Reads a string with the length as the first two bytes
inline std::string deSerializeString(std::istream &is) inline std::string deSerializeString(std::istream &is)
@ -2014,6 +2028,27 @@ inline std::string deSerializeString(std::istream &is)
return s; return s;
} }
// Reads a wide string with the length as the first two bytes
inline std::wstring deSerializeWideString(std::istream &is)
{
char buf[2];
is.read(buf, 2);
if(is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
u16 s_size = readU16((u8*)buf);
if(s_size == 0)
return L"";
std::wstring s;
s.reserve(s_size);
for(u32 i=0; i<s_size; i++)
{
is.read(&buf[0], 2);
wchar_t c16 = readU16((u8*)buf);
s.append(&c16, 1);
}
return s;
}
// Creates a string with the length as the first four bytes // Creates a string with the length as the first four bytes
inline std::string serializeLongString(const std::string &plain) inline std::string serializeLongString(const std::string &plain)
{ {
@ -2025,18 +2060,6 @@ inline std::string serializeLongString(const std::string &plain)
return s; return s;
} }
/*// Reads a string with the length as the first four bytes
inline std::string deSerializeLongString(const std::string encoded)
{
u32 s_size = readU32((u8*)&encoded.c_str()[0]);
if(s_size > encoded.length() - 4)
return "";
std::string s;
s.reserve(s_size);
s.append(&encoded.c_str()[4], s_size);
return s;
}*/
// Reads a string with the length as the first four bytes // Reads a string with the length as the first four bytes
inline std::string deSerializeLongString(std::istream &is) inline std::string deSerializeLongString(std::istream &is)
{ {

@ -19,10 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h" #include "voxel.h"
#include "map.h" #include "map.h"
#include "utility.h" // For TimeTaker
// For TimeTaker
#include "utility.h"
#include "gettime.h" #include "gettime.h"
#include "content_mapnode.h"
/* /*
Debug stuff Debug stuff

@ -384,6 +384,14 @@ public:
return m_data[m_area.index(p)]; return m_data[m_area.index(p)];
} }
MapNode getNodeNoExNoEmerge(v3s16 p)
{
if(m_area.contains(p) == false)
return MapNode(CONTENT_IGNORE);
if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
return MapNode(CONTENT_IGNORE);
return m_data[m_area.index(p)];
}
MapNode & getNodeRef(v3s16 p) MapNode & getNodeRef(v3s16 p)
{ {
emerge(p); emerge(p);

21
util/colors.txt Normal file

@ -0,0 +1,21 @@
0 128 128 128
1 107 134 51
2 39 66 106
3 255 255 0
4 86 58 31
5 48 95 8
6 102 129 38
7 178 178 0
8 101 84 36
9 39 66 106
12 104 78 42
13 210 194 156
14 117 86 41
15 128 79 0
16 118 118 118
18 123 123 123
19 199 199 199
20 183 183 222
21 103 78 42
22 108 138 108
23 90 90 90

271
util/genmap.py Executable file

@ -0,0 +1,271 @@
#!/usr/bin/python2
# This is an example script that generates some valid map data.
import struct
import random
import os
import sys
import zlib
import array
from pnoise import pnoise
# Old directory format:
# world/sectors/XXXXZZZZ/YYYY
# XXXX,YYYY,ZZZZ = coordinates in hexadecimal
# fffe = -2
# ffff = -1
# 0000 = 0
# 0001 = 1
#
# New directory format:
# world/sectors2/XXX/ZZZ/YYYY
# XXX,YYYY,ZZZ = coordinates in hexadecimal
# fffe = -2
# ffff = -1
# 0000 = 0
# 0001 = 1
# ffe = -2
# fff = -1
# 000 = 0
# 001 = 1
#
# For more proper file format documentation, refer to mapformat.txt
# For node type documentation, refer to mapnode.h
# NodeMetadata documentation is not complete, refer to nodemeta.cpp
#
# Seed for generating terrain
SEED = 0
# 0=old, 1=new
SECTOR_DIR_FORMAT = 1
mapdir = "../world"
def to4h(i):
s = "";
s += '{0:1x}'.format((i>>12) & 0x000f)
s += '{0:1x}'.format((i>>8) & 0x000f)
s += '{0:1x}'.format((i>>4) & 0x000f)
s += '{0:1x}'.format((i>>0) & 0x000f)
return s
def to3h(i):
s = "";
s += '{0:1x}'.format((i>>8) & 0x000f)
s += '{0:1x}'.format((i>>4) & 0x000f)
s += '{0:1x}'.format((i>>0) & 0x000f)
return s
def get_sector_dir(px, pz):
global SECTOR_DIR_FORMAT
if SECTOR_DIR_FORMAT == 0:
return "/sectors/"+to4h(px)+to4h(pz)
elif SECTOR_DIR_FORMAT == 1:
return "/sectors2/"+to3h(px)+"/"+to3h(pz)
else:
assert(0)
def getrand_air_stone():
i = random.randrange(0,2)
if i==0:
return 0
return 254
# 3-dimensional vector (position)
class v3:
def __init__(self, x=0, y=0, z=0):
self.X = x
self.Y = y
self.Z = z
class NodeMeta:
def __init__(self, type_id, data):
self.type_id = type_id
self.data = data
class StaticObject:
def __init__(self):
self.type_id = 0
self.data = ""
def ser_u16(i):
return chr((i>>8)&0xff) + chr((i>>0)&0xff)
def ser_u32(i):
return (chr((i>>24)&0xff) + chr((i>>16)&0xff)
+ chr((i>>8)&0xff) + chr((i>>0)&0xff))
# A 16x16x16 chunk of map
class MapBlock:
def __init__(self):
self.content = array.array('B')
self.param1 = array.array('B')
self.param2 = array.array('B')
for i in range(16*16*16):
# Initialize to air
self.content.append(254)
# Full light on sunlight, none when no sunlight
self.param1.append(15)
# No additional parameters
self.param2.append(0)
# key = v3 pos
# value = NodeMeta
self.nodemeta = {}
# key = v3 pos
# value = StaticObject
self.static_objects = {}
def set_content(self, v3, b):
self.content[v3.Z*16*16+v3.Y*16+v3.X] = b
def set_param1(self, v3, b):
self.param1[v3.Z*16*16+v3.Y*16+v3.X] = b
def set_param2(self, v3, b):
self.param2[v3.Z*16*16+v3.Y*16+v3.X] = b
# Get data for serialization. Returns a string.
def serialize_data(self):
s = ""
for i in range(16*16*16):
s += chr(self.content[i])
for i in range(16*16*16):
s += chr(self.param1[i])
for i in range(16*16*16):
s += chr(self.param2[i])
return s
def serialize_nodemeta(self):
s = ""
s += ser_u16(1)
s += ser_u16(len(self.nodemeta))
for pos, meta in self.nodemeta.items():
pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
s += ser_u16(pos_i)
s += ser_u16(meta.type_id)
s += ser_u16(len(meta.data))
s += meta.data
return s
def serialize_staticobj(self):
s = ""
s += chr(0)
s += ser_u16(len(self.static_objects))
for pos, obj in self.static_objects.items():
pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
s += ser_s32(pos.X*1000)
s += ser_s32(pos.Y*1000)
s += ser_s32(pos.Z*1000)
s += ser_u16(obj.type_id)
s += ser_u16(len(obj.data))
s += obj.data
return s
def writeblock(mapdir, px,py,pz, block):
sectordir = mapdir + get_sector_dir(px, pz);
try:
os.makedirs(sectordir)
except OSError:
pass
path = sectordir+"/"+to4h(py)
print("writing block file "+path)
f = open(sectordir+"/"+to4h(py), "wb")
if f == None:
return
# version
version = 17
f.write(struct.pack('B', version))
# flags
# 0x01=is_undg, 0x02=dn_diff, 0x04=lighting_expired
flags = 0 + 0x02 + 0x04
f.write(struct.pack('B', flags))
# data
c_obj = zlib.compressobj()
c_obj.compress(block.serialize_data())
f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
f.write(c_obj.flush())
# node metadata
c_obj = zlib.compressobj()
c_obj.compress(block.serialize_nodemeta())
f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
f.write(c_obj.flush())
# mapblockobject count
f.write(ser_u16(0))
# static objects
f.write(block.serialize_staticobj())
# timestamp
f.write(ser_u32(0xffffffff))
f.close()
for z0 in range(-1,3):
for x0 in range(-1,3):
for y0 in range(-1,3):
print("generating block "+str(x0)+","+str(y0)+","+str(z0))
#v3 blockp = v3(x0,y0,z0)
# Create a MapBlock
block = MapBlock()
# Generate stuff in it
for z in range(0,16):
for x in range(0,16):
h = 20.0*pnoise((x0*16+x)/100.,(z0*16+z)/100.,SEED+0)
h += 5.0*pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+0)
if pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+92412) > 0.05:
h += 10
#print("r="+str(r))
# This enables comparison by ==
h = int(h)
for y in range(0,16):
p = v3(x,y,z)
b = 254
y1 = y0*16+y
if y1 <= h-3:
b = 0 #stone
elif y1 <= h and y1 <= 0:
b = 8 #mud
elif y1 == h:
b = 1 #grass
elif y1 < h:
b = 8 #mud
elif y1 <= 1:
b = 9 #water
# Material content
block.set_content(p, b)
# Place a sign at the center at surface level.
# Placing a sign means placing the sign node and
# adding node metadata to the mapblock.
if x == 8 and z == 8 and y0*16 <= h-1 and (y0+1)*16-1 > h:
p = v3(8,h+1-y0*16,8)
# 14 = Sign
content_type = 14
block.set_content(p, content_type)
# This places the sign to the bottom of the cube.
# Working values: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20
block.set_param2(p, 0x08)
# Then add metadata to hold the text of the sign
s = "Hello at sector ("+str(x0)+","+str(z0)+")"
meta = NodeMeta(content_type, ser_u16(len(s))+s)
block.nodemeta[p] = meta
# Write it on disk
writeblock(mapdir, x0,y0,z0, block)
#END

444
util/minetestmapper.py Executable file

@ -0,0 +1,444 @@
#!/usr/bin/python2
# -*- coding: windows-1252 -*-
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# COPYING for more details.
# Made by Jogge, modified by celeron55
# 2011-05-29: j0gge: initial release
# 2011-05-30: celeron55: simultaneous support for sectors/sectors2, removed
# 2011-06-02: j0gge: command line parameters, coordinates, players, ...
# 2011-06-04: celeron55: added #!/usr/bin/python2 and converted \r\n to \n
# to make it easily executable on Linux
# Requires Python Imaging Library: http://www.pythonware.com/products/pil/
# Some speed-up: ...lol, actually it slows it down.
#import psyco ; psyco.full()
#from psyco.classes import *
import zlib
import Image, ImageDraw, ImageFont, ImageColor
import os
import string
import time
import getopt
import sys
def hex_to_int(h):
i = int(h, 16)
if(i > 2047):
i -= 4096
return i
def hex4_to_int(h):
i = int(h, 16)
if(i > 32767):
i -= 65536
return i
def int_to_hex3(i):
if(i < 0):
return "%03X" % (i + 4096)
else:
return "%03X" % i
def int_to_hex4(i):
if(i < 0):
return "%04X" % (i + 65536)
else:
return "%04X" % i
def limit(i, l, h):
if(i > h):
i = h
if(i < l):
i = l
return i
def usage():
print "TODO: Help"
try:
opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", "output=", "bgcolor=", "scalecolor=", "origincolor=", "playercolor=", "draworigin", "drawplayers", "drawscale"])
except getopt.GetoptError, err:
# print help information and exit:
print str(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
path = "../world/"
output = "uloste.png"
border = 0
scalecolor = "black"
bgcolor = "white"
origincolor = "red"
playercolor = "red"
drawscale = False
drawplayers = False
draworigin = False
sector_xmin = -1500 / 16
sector_xmax = 1500 / 16
sector_zmin = -1500 / 16
sector_zmax = 1500 / 16
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("-i", "--input"):
path = a
elif o in ("-o", "--output"):
output = a
elif o == "--bgcolor":
bgcolor = ImageColor.getrgb(a)
elif o == "--scalecolor":
scalecolor = ImageColor.getrgb(a)
elif o == "--playercolor":
playercolor = ImageColor.getrgb(a)
elif o == "--origincolor":
origincolor = ImageColor.getrgb(a)
elif o == "--drawscale":
drawscale = True
border = 40
elif o == "--drawplayers":
drawplayers = True
elif o == "--draworigin":
draworigin = True
else:
assert False, "unhandled option"
if path[-1:]!="/" and path[-1:]!="\\":
path = path + "/"
# Load color information for the blocks.
colors = {}
f = file("colors.txt")
for line in f:
values = string.split(line)
colors[int(values[0])] = (int(values[1]), int(values[2]), int(values[3]))
f.close()
xlist = []
zlist = []
# List all sectors to memory and calculate the width and heigth of the resulting picture.
try:
for filename in os.listdir(path + "sectors2"):
for filename2 in os.listdir(path + "sectors2/" + filename):
x = hex_to_int(filename)
z = hex_to_int(filename2)
if x < sector_xmin or x > sector_xmax:
continue
if z < sector_zmin or z > sector_zmax:
continue
xlist.append(x)
zlist.append(z)
except OSError:
pass
try:
for filename in os.listdir(path + "sectors"):
x = hex4_to_int(filename[:4])
z = hex4_to_int(filename[-4:])
if x < sector_xmin or x > sector_xmax:
continue
if z < sector_zmin or z > sector_zmax:
continue
xlist.append(x)
zlist.append(z)
except OSError:
pass
minx = min(xlist)
minz = min(zlist)
maxx = max(xlist)
maxz = max(zlist)
w = (maxx - minx) * 16 + 16
h = (maxz - minz) * 16 + 16
print "w="+str(w)+" h="+str(h)
im = Image.new("RGB", (w + border, h + border), bgcolor)
draw = ImageDraw.Draw(im)
impix = im.load()
stuff = {}
starttime = time.time()
# Go through all sectors.
for n in range(len(xlist)):
#if n > 500:
# break
if n % 200 == 0:
nowtime = time.time()
dtime = nowtime - starttime
try:
n_per_second = 1.0 * n / dtime
except ZeroDivisionError:
n_per_second = 0
if n_per_second != 0:
seconds_per_n = 1.0 / n_per_second
time_guess = seconds_per_n * len(xlist)
remaining_s = time_guess - dtime
remaining_minutes = int(remaining_s / 60)
remaining_s -= remaining_minutes * 60;
print("Processing sector "+str(n)+" of "+str(len(xlist))
+" ("+str(round(100.0*n/len(xlist), 1))+"%)"
+" (ETA: "+str(remaining_minutes)+"m "
+str(int(remaining_s))+"s)")
xpos = xlist[n]
zpos = zlist[n]
xhex = int_to_hex3(xpos)
zhex = int_to_hex3(zpos)
xhex4 = int_to_hex4(xpos)
zhex4 = int_to_hex4(zpos)
sector1 = xhex4.lower() + zhex4.lower()
sector2 = xhex.lower() + "/" + zhex.lower()
ylist = []
sectortype = ""
try:
for filename in os.listdir(path + "sectors/" + sector1):
if(filename != "meta"):
pos = int(filename, 16)
if(pos > 32767):
pos -= 65536
ylist.append(pos)
sectortype = "old"
except OSError:
pass
if sectortype != "old":
try:
for filename in os.listdir(path + "sectors2/" + sector2):
if(filename != "meta"):
pos = int(filename, 16)
if(pos > 32767):
pos -= 65536
ylist.append(pos)
sectortype = "new"
except OSError:
pass
if sectortype == "":
continue
ylist.sort()
# Make a list of pixels of the sector that are to be looked for.
pixellist = []
water = {}
for x in range(16):
for z in range(16):
pixellist.append((x, z))
water[(x, z)] = 0
# Go through the Y axis from top to bottom.
ylist2=[]
for ypos in reversed(ylist):
yhex = int_to_hex4(ypos)
filename = ""
if sectortype == "old":
filename = path + "sectors/" + sector1 + "/" + yhex.lower()
else:
filename = path + "sectors2/" + sector2 + "/" + yhex.lower()
f = file(filename, "rb")
version = f.read(1)
flags = f.read(1)
# Checking day and night differs -flag
if not ord(flags) & 2:
ylist2.append((ypos,filename))
f.close()
continue
dec_o = zlib.decompressobj()
try:
mapdata = dec_o.decompress(f.read())
except:
mapdata = []
f.close()
if(len(mapdata) < 4096):
print "bad: " + xhex + "/" + zhex + "/" + yhex + " " + str(len(mapdata))
else:
chunkxpos = xpos * 16
chunkypos = ypos * 16
chunkzpos = zpos * 16
for (x, z) in reversed(pixellist):
for y in reversed(range(16)):
datapos = x + y * 16 + z * 256
if(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) in colors):
if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9):
water[(x, z)] += 1
# Add dummy stuff for drawing sea without seabed
stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
else:
pixellist.remove((x, z))
# Memorize information on the type and height of the block and for drawing the picture.
stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
break
elif(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) not in colors):
print "strange block: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos]))
# After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis.
if(len(pixellist) == 0):
break
if len(pixellist) > 0:
for (ypos, filename) in ylist2:
f = file(filename, "rb")
version = f.read(1)
flags = f.read(1)
dec_o = zlib.decompressobj()
try:
mapdata = dec_o.decompress(f.read())
except:
mapdata = []
f.close()
if(len(mapdata) < 4096):
print "bad: " + xhex + "/" + zhex + "/" + yhex + " " + str(len(mapdata))
else:
chunkxpos = xpos * 16
chunkypos = ypos * 16
chunkzpos = zpos * 16
for (x, z) in reversed(pixellist):
for y in reversed(range(16)):
datapos = x + y * 16 + z * 256
if(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) in colors):
if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9):
water[(x, z)] += 1
# Add dummy stuff for drawing sea without seabed
stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
else:
pixellist.remove((x, z))
# Memorize information on the type and height of the block and for drawing the picture.
stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
break
elif(ord(mapdata[datapos]) != 254 and ord(mapdata[datapos]) not in colors):
print "outo palikka: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos]))
# After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis.
if(len(pixellist) == 0):
break
print "Drawing image"
# Drawing the picture
starttime = time.time()
n = 0
for (x, z) in stuff.iterkeys():
if n % 500000 == 0:
nowtime = time.time()
dtime = nowtime - starttime
try:
n_per_second = 1.0 * n / dtime
except ZeroDivisionError:
n_per_second = 0
if n_per_second != 0:
listlen = len(stuff)
seconds_per_n = 1.0 / n_per_second
time_guess = seconds_per_n * listlen
remaining_s = time_guess - dtime
remaining_minutes = int(remaining_s / 60)
remaining_s -= remaining_minutes * 60;
print("Drawing pixel "+str(n)+" of "+str(listlen)
+" ("+str(round(100.0*n/listlen, 1))+"%)"
+" (ETA: "+str(remaining_minutes)+"m "
+str(int(remaining_s))+"s)")
n += 1
(r, g, b) = colors[stuff[(x,z)][1]]
# Comparing heights of a couple of adjacent blocks and changing brightness accordingly.
try:
c1 = stuff[(x - 1, z)][1]
c2 = stuff[(x, z + 1)][1]
c = stuff[(x, z)][1]
if c1 != 2 and c1 != 9 and c2 != 2 and c2 != 9 and c != 2 and c != 9:
y1 = stuff[(x - 1, z)][0]
y2 = stuff[(x, z + 1)][0]
y = stuff[(x, z)][0]
d = ((y - y1) + (y - y2)) * 12
else:
d = 0
if(d > 36):
d = 36
r = limit(r + d, 0, 255)
g = limit(g + d, 0, 255)
b = limit(b + d, 0, 255)
except:
pass
# Water
if(stuff[(x,z)][2] > 0):
r=int(r * .15 + colors[2][0] * .85)
g=int(g * .15 + colors[2][1] * .85)
b=int(b * .15 + colors[2][2] * .85)
impix[x - minx * 16 + border, h - 1 - (z - minz * 16) + border] = (r, g, b)
if draworigin:
draw.ellipse((minx * -16 - 5 + border, h - minz * -16 - 6 + border, minx * -16 + 5 + border, h - minz * -16 + 4 + border), outline = origincolor)
font = ImageFont.load_default()
if drawscale:
draw.text((24, 0), "X", font = font, fill = scalecolor)
draw.text((2, 24), "Z", font = font, fill = scalecolor)
for n in range(int(minx / -4) * -4, maxx, 4):
draw.text((minx * -16 + n * 16 + 2 + border, 0), str(n * 16), font = font, fill = scalecolor)
draw.line((minx * -16 + n * 16 + border, 0, minx * -16 + n * 16 + border, border - 1), fill = scalecolor)
for n in range(int(maxz / 4) * 4, minz, -4):
draw.text((2, h - 1 - (n * 16 - minz * 16) + border), str(n * 16), font = font, fill = scalecolor)
draw.line((0, h - 1 - (n * 16 - minz * 16) + border, border - 1, h - 1 - (n * 16 - minz * 16) + border), fill = scalecolor)
if drawplayers:
try:
for filename in os.listdir(path + "players"):
f = file(path + "players/" + filename)
lines = f.readlines()
name=""
position=[]
for line in lines:
p = string.split(line)
if p[0] == "name":
name = p[2]
print filename + ": name = " + name
if p[0] == "position":
position = string.split(p[2][1:-1], ",")
print filename + ": position = " + p[2]
if len(name) > 0 and len(position) == 3:
x=(int(float(position[0]) / 10 - minx * 16))
z=int(h - (float(position[2]) / 10 - minz * 16))
draw.ellipse((x - 2 + border, z - 2 + border, x + 2 + border, z + 2 + border), outline = playercolor)
draw.text((x + 2 + border, z + 2 + border), name, font = font, fill = playercolor)
f.close()
except OSError:
pass
print "Saving"
im.save(output)

102
util/pnoise.py Normal file

@ -0,0 +1,102 @@
#
# A python perlin noise implementation, from
# http://www.fundza.com/c4serious/noise/perlin/perlin.html
#
# This is used for testing how to create maps with a python script.
#
import math
p = (
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,
30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,
62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,
125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,
83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,
143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,
196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,
58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,
221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,
224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,
12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,
199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,
205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180)
def lerp(t, a, b):
return a + t * (b - a)
def fade(t):
return t * t * t * (t * (t * 6 - 15) + 10)
def grad(hash, x, y, z):
h = hash & 15
if h < 8:
u = x
else:
u = y
if h < 4:
v = y
elif h == 12 or h == 14:
v = x
else:
v = z
if h & 1 != 0:
u = -u
if h & 2 != 0:
v = -v
return u + v
def pnoise(x, y, z):
global p
X = int(math.floor(x)) & 255
Y = int(math.floor(y)) & 255
Z = int(math.floor(z)) & 255
x -= math.floor(x)
y -= math.floor(y)
z -= math.floor(z)
u = fade(x)
v = fade(y)
w = fade(z)
A = p[X] + Y
AA = p[A] + Z
AB = p[A + 1] + Z
B = p[X + 1] + Y
BA = p[B] + Z
BB = p[B + 1] + Z
pAA = p[AA]
pAB = p[AB]
pBA = p[BA]
pBB = p[BB]
pAA1 = p[AA + 1]
pBA1 = p[BA + 1]
pAB1 = p[AB + 1]
pBB1 = p[BB + 1]
gradAA = grad(pAA, x, y, z)
gradBA = grad(pBA, x-1, y, z)
gradAB = grad(pAB, x, y-1, z)
gradBB = grad(pBB, x-1, y-1, z)
gradAA1 = grad(pAA1,x, y, z-1)
gradBA1 = grad(pBA1,x-1, y, z-1)
gradAB1 = grad(pAB1,x, y-1, z-1)
gradBB1 = grad(pBB1,x-1, y-1, z-1)
return lerp(w,
lerp(v, lerp(u, gradAA, gradBA), lerp(u, gradAB, gradBB)),
lerp(v, lerp(u, gradAA1,gradBA1),lerp(u, gradAB1,gradBB1)))