aboutsummaryrefslogtreecommitdiff
path: root/src/network/address.cpp
blob: f698a2e91b7eb24e46aa9f0e299878dfa5e9180a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
Minetest
Copyright (C) 2013 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 Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "address.h"

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sstream>
#include <iomanip>
#include "network/networkexceptions.h"
#include "util/string.h"
#include "util/numeric.h"
#include "constants.h"
#include "debug.h"
#include "settings.h"
#include "log.h"

#ifdef _WIN32
// Without this some of the network functions are not found on mingw
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define LAST_SOCKET_ERR() WSAGetLastError()
typedef SOCKET socket_t;
typedef int socklen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#define LAST_SOCKET_ERR() (errno)
typedef int socket_t;
#endif

/*
	Address
*/

Address::Address()
{
	memset(&m_address, 0, sizeof(m_address));
}

Address::Address(u32 address, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(address);
	setPort(port);
}

Address::Address(u8 a, u8 b, u8 c, u8 d, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(a, b, c, d);
	setPort(port);
}

Address::Address(const IPv6AddressBytes *ipv6_bytes, u16 port)
{
	memset(&m_address, 0, sizeof(m_address));
	setAddress(ipv6_bytes);
	setPort(port);
}

// Equality (address family, address and port must be equal)
bool Address::operator==(const Address &address)
{
	if (address.m_addr_family != m_addr_family || address.m_port != m_port)
		return false;

	if (m_addr_family == AF_INET) {
		return m_address.ipv4.sin_addr.s_addr ==
		       address.m_address.ipv4.sin_addr.s_addr;
	}

	if (m_addr_family == AF_INET6) {
		return memcmp(m_address.ipv6.sin6_addr.s6_addr,
				       address.m_address.ipv6.sin6_addr.s6_addr, 16) == 0;
	}

	return false;
}

bool Address::operator!=(const Address &address)
{
	return !(*this == address);
}

void Address::Resolve(const char *name)
{
	if (!name || name[0] == 0) {
		if (m_addr_family == AF_INET) {
			setAddress((u32)0);
		} else if (m_addr_family == AF_INET6) {
			setAddress((IPv6AddressBytes *)0);
		}
		return;
	}

	struct addrinfo *resolved, hints;
	memset(&hints, 0, sizeof(hints));

	// Setup hints
	hints.ai_socktype = 0;
	hints.ai_protocol = 0;
	hints.ai_flags = 0;
	if (g_settings->getBool("enable_ipv6")) {
		// AF_UNSPEC allows both IPv6 and IPv4 addresses to be returned
		hints.ai_family = AF_UNSPEC;
	} else {
		hints.ai_family = AF_INET;
	}

	// Do getaddrinfo()
	int e = getaddrinfo(name, NULL, &hints, &resolved);
	if (e != 0)
		throw ResolveError(gai_strerror(e));

	// Copy data
	if (resolved->ai_family == AF_INET) {
		struct sockaddr_in *t = (struct sockaddr_in *)resolved->ai_addr;
		m_addr_family = AF_INET;
		m_address.ipv4 = *t;
	} else if (resolved->ai_family == AF_INET6) {
		struct sockaddr_in6 *t = (struct sockaddr_in6 *)resolved->ai_addr;
		m_addr_family = AF_INET6;
		m_address.ipv6 = *t;
	} else {
		freeaddrinfo(resolved);
		throw ResolveError("");
	}
	freeaddrinfo(resolved);
}

// IP address -> textual representation
std::string Address::serializeString() const
{
// windows XP doesnt have inet_ntop, maybe use better func
#ifdef _WIN32
	if (m_addr_family == AF_INET) {
		u8 a, b, c, d;
		u32 addr;
		addr = ntohl(m_address.ipv4.sin_addr.s_addr);
		a = (addr & 0xFF000000) >> 24;
		b = (addr & 0x00FF0000) >> 16;
		c = (addr & 0x0000FF00) >> 8;
		d = (addr & 0x000000FF);
		return itos(a) + "." + itos(b) + "." + itos(c) + "." + itos(d);
	} else if (m_addr_family == AF_INET6) {
		std::ostringstream os;
		for (int i = 0; i < 16; i += 2) {
			u16 section = (m_address.ipv6.sin6_addr.s6_addr[i] << 8) |
				      (m_address.ipv6.sin6_addr.s6_addr[i + 1]);
			os << std::hex << section;
			if (i < 14)
				os << ":";
		}
		return os.str();
	} else
		return std::string("");
#else
	char str[INET6_ADDRSTRLEN];
	if (inet_ntop(m_addr_family,
			    (m_addr_family == AF_INET)
					    ? (void *)&(m_address.ipv4.sin_addr)
					    : (void *)&(m_address.ipv6.sin6_addr),
			    str, INET6_ADDRSTRLEN) == NULL) {
		return std::string("");
	}
	return std::string(str);
#endif
}

struct sockaddr_in Address::getAddress() const
{
	return m_address.ipv4; // NOTE: NO PORT INCLUDED, use getPort()
}

struct sockaddr_in6 Address::getAddress6() const
{
	return m_address.ipv6; // NOTE: NO PORT INCLUDED, use getPort()
}

u16 Address::getPort() const
{
	return m_port;
}

int Address::getFamily() const
{
	return m_addr_family;
}

bool Address::isIPv6() const
{
	return m_addr_family == AF_INET6;
}

bool Address::isZero() const
{
	if (m_addr_family == AF_INET) {
		return m_address.ipv4.sin_addr.s_addr == 0;
	}

	if (m_addr_family == AF_INET6) {
		static const char zero[16] = {0};
		return memcmp(m_address.ipv6.sin6_addr.s6_addr, zero, 16) == 0;
	}
	return false;
}

void Address::setAddress(u32 address)
{
	m_addr_family = AF_INET;
	m_address.ipv4.sin_family = AF_INET;
	m_address.ipv4.sin_addr.s_addr = htonl(address);
}

void Address::setAddress(u8 a, u8 b, u8 c, u8 d)
{
	m_addr_family = AF_INET;
	m_address.ipv4.sin_family = AF_INET;
	u32 addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
	m_address.ipv4.sin_addr.s_addr = addr;
}

void Address::setAddress(const IPv6AddressBytes *ipv6_bytes)
{
	m_addr_family = AF_INET6;
	m_address.ipv6.sin6_family = AF_INET6;
	if (ipv6_bytes)
		memcpy(m_address.ipv6.sin6_addr.s6_addr, ipv6_bytes->bytes, 16);
	else
		memset(m_address.ipv6.sin6_addr.s6_addr, 0, 16);
}

void Address::setPort(u16 port)
{
	m_port = port;
}

void Address::print(std::ostream *s) const
{
	if (m_addr_family == AF_INET6)
		*s << "[" << serializeString() << "]:" << m_port;
	else
		*s << serializeString() << ":" << m_port;
}
n> is_under_river = false; // Is column under river water bool is_under_tunnel = false; // Is tunnel or is under tunnel bool is_top_filler_above = false; // Is top or filler above node // Indexes at column top u32 vi = vm->m_area.index(x, nmax.Y, z); u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + (x - nmin.X); // 3D noise index // Biome of column Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]); u16 depth_top = biome->depth_top; u16 base_filler = depth_top + biome->depth_filler; u16 depth_riverbed = biome->depth_riverbed; u16 nplaced = 0; // Don't excavate the overgenerated stone at nmax.Y + 1, // this creates a 'roof' over the tunnel, preventing light in // tunnels at mapchunk borders when generating mapchunks upwards. // This 'roof' is removed when the mapchunk above is generated. for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, index3d -= m_ystride, VoxelArea::add_y(em, vi, -1)) { content_t c = vm->m_data[vi].getContent(); if (c == CONTENT_AIR || c == biome->c_water_top || c == biome->c_water) { column_is_open = true; is_top_filler_above = false; continue; } if (c == biome->c_river_water) { column_is_open = true; is_under_river = true; is_top_filler_above = false; continue; } // Ground float d1 = contour(noise_cave1->result[index3d]); float d2 = contour(noise_cave2->result[index3d]); if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) { // In tunnel and ground content, excavate vm->m_data[vi] = MapNode(CONTENT_AIR); is_under_tunnel = true; // If tunnel roof is top or filler, replace with stone if (is_top_filler_above) vm->m_data[vi + em.X] = MapNode(biome->c_stone); is_top_filler_above = false; } else if (column_is_open && is_under_tunnel && (c == biome->c_stone || c == biome->c_filler)) { // Tunnel entrance floor, place biome surface nodes if (is_under_river) { if (nplaced < depth_riverbed) { vm->m_data[vi] = MapNode(biome->c_riverbed); is_top_filler_above = true; nplaced++; } else { // Disable top/filler placement column_is_open = false; is_under_river = false; is_under_tunnel = false; } } else if (nplaced < depth_top) { vm->m_data[vi] = MapNode(biome->c_top); is_top_filler_above = true; nplaced++; } else if (nplaced < base_filler) { vm->m_data[vi] = MapNode(biome->c_filler); is_top_filler_above = true; nplaced++; } else { // Disable top/filler placement column_is_open = false; is_under_tunnel = false; } } else { // Not tunnel or tunnel entrance floor // Check node for possible replacing with stone for tunnel roof if (c == biome->c_top || c == biome->c_filler) is_top_filler_above = true; column_is_open = false; } } } } //// //// CavernsNoise //// CavernsNoise::CavernsNoise( const NodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern, s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold) { assert(nodedef); m_ndef = nodedef; m_csize = chunksize; m_cavern_limit = cavern_limit; m_cavern_taper = cavern_taper; m_cavern_threshold = cavern_threshold; m_ystride = m_csize.X; m_zstride_1d = m_csize.X * (m_csize.Y + 1); // Noise is created using 1-down overgeneration // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for // re-carving the solid overtop placed for blocking sunlight noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); c_water_source = m_ndef->getId("mapgen_water_source"); if (c_water_source == CONTENT_IGNORE) c_water_source = CONTENT_AIR; c_lava_source = m_ndef->getId("mapgen_lava_source"); if (c_lava_source == CONTENT_IGNORE) c_lava_source = CONTENT_AIR; } CavernsNoise::~CavernsNoise() { delete noise_cavern; } bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax) { assert(vm); // Calculate noise noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); // Cache cavern_amp values float *cavern_amp = new float[m_csize.Y + 1]; u8 cavern_amp_index = 0; // Index zero at column top for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) { cavern_amp[cavern_amp_index] = MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f); } //// Place nodes bool near_cavern = false; const v3s16 &em = vm->m_area.getExtent(); u32 index2d = 0; for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { // Reset cave_amp index to column top cavern_amp_index = 0; // Initial voxelmanip index at column top u32 vi = vm->m_area.index(x, nmax.Y, z); // Initial 3D noise index at column top u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + (x - nmin.X); // Don't excavate the overgenerated stone at node_max.Y + 1, // this creates a 'roof' over the cavern, preventing light in // caverns at mapchunk borders when generating mapchunks upwards. // This 'roof' is excavated when the mapchunk above is generated. for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, index3d -= m_ystride, VoxelArea::add_y(em, vi, -1), cavern_amp_index++) { content_t c = vm->m_data[vi].getContent(); float n_absamp_cavern = fabs(noise_cavern->result[index3d]) * cavern_amp[cavern_amp_index]; // Disable CavesRandomWalk at a safe distance from caverns // to avoid excessively spreading liquids in caverns. if (n_absamp_cavern > m_cavern_threshold - 0.1f) { near_cavern = true; if (n_absamp_cavern > m_cavern_threshold && m_ndef->get(c).is_ground_content) vm->m_data[vi] = MapNode(CONTENT_AIR); } } } delete[] cavern_amp; return near_cavern; } //// //// CavesRandomWalk //// CavesRandomWalk::CavesRandomWalk( const NodeDefManager *ndef, GenerateNotifier *gennotify, s32 seed, int water_level, content_t water_source, content_t lava_source, int lava_depth) { assert(ndef); this->ndef = ndef; this->gennotify = gennotify; this->seed = seed; this->water_level = water_level; this->np_caveliquids = &nparams_caveliquids; this->lava_depth = lava_depth; c_water_source = water_source; if (c_water_source == CONTENT_IGNORE) c_water_source = ndef->getId("mapgen_water_source"); if (c_water_source == CONTENT_IGNORE) c_water_source = CONTENT_AIR; c_lava_source = lava_source; if (c_lava_source == CONTENT_IGNORE) c_lava_source = ndef->getId("mapgen_lava_source"); if (c_lava_source == CONTENT_IGNORE) c_lava_source = CONTENT_AIR; } void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap) { assert(vm); assert(ps); this->vm = vm; this->ps = ps; this->node_min = nmin; this->node_max = nmax; this->heightmap = heightmap; this->large_cave = is_large_cave; this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness int dswitchint = ps->range(1, 14); flooded = ps->range(1, 2) == 2; if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); min_tunnel_diameter = 5; max_tunnel_diameter = ps->range(7, ps->range(8, 24)); } else { part_max_length_rs = ps->range(2, 9); tunnel_routepoints = ps->range(10, ps->range(15, 30)); min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); } large_cave_is_flat = (ps->range(0, 1) == 0); main_direction = v3f(0, 0, 0); // Allowed route area size in nodes ar = node_max - node_min + v3s16(1, 1, 1); // Area starting point in nodes of = node_min; // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 insure = 10; s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); if (large_cave) { s16 minpos = 0; if (node_min.Y < water_level && node_max.Y > water_level) { minpos = water_level - max_tunnel_diameter / 3 - of.Y; route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; } route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position orp.Z = (float)(ps->next() % ar.Z) + 0.5f; orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; gennotify->addEvent(notifytype, abs_pos); } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); } } void CavesRandomWalk::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; main_direction *= (float)ps->range(0, 10) / 10; } // Randomize size s16 min_d = min_tunnel_diameter; s16 max_d = max_tunnel_diameter; rs = ps->range(min_d, max_d); s16 rs_part_max_length_rs = rs * part_max_length_rs; v3s16 maxlen; if (large_cave) { maxlen = v3s16( rs_part_max_length_rs, rs_part_max_length_rs / 2, rs_part_max_length_rs ); } else { maxlen = v3s16( rs_part_max_length_rs, ps->range(1, rs_part_max_length_rs), rs_part_max_length_rs ); } v3f vec; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } else { vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2; vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } // Do not make caves that are above ground. // It is only necessary to check the startpoint and endpoint. v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; if (isPosAboveSurface(p1) || isPosAboveSurface(p2)) return; vec += main_direction; v3f rp = orp + vec; if (rp.X < 0) rp.X = 0; else if (rp.X >= ar.X) rp.X = ar.X - 1; if (rp.Y < route_y_min) rp.Y = route_y_min; else if (rp.Y >= route_y_max) rp.Y = route_y_max - 1; if (rp.Z < 0) rp.Z = 0; else if (rp.Z >= ar.Z) rp.Z = ar.Z - 1; vec = rp - orp; float veclen = vec.getLength(); if (veclen < 0.05f) veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps->range(1, 2) == 1); // Carve routes for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz); orp = rp; } void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); MapNode lavanode(c_lava_source); v3s16 startp(orp.X, orp.Y, orp.Z); startp += of; float nval = NoisePerlin3D(np_caveliquids, startp.X, startp.Y, startp.Z, seed); MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ? lavanode : waternode; v3f fp = orp + vec * f; fp.X += 0.1f * ps->range(-10, 10); fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); d1 += ps->range(-1, 1); } bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2; for (s16 z0 = d0; z0 <= d1; z0++) { s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { s16 maxabsxz = MYMAX(abs(x0), abs(z0)); s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); for (s16 y0 = -si2; y0 <= si2; y0++) { // Make better floors in small caves if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7) continue; if (large_cave_is_flat) { // Make large caves not so tall if (rs > 7 && abs(y0) >= rs / 3) continue; } v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; if (!vm->m_area.contains(p)) continue; u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (!ndef->get(c).is_ground_content) continue; if (large_cave) { int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; if (flooded && full_ymin < water_level && full_ymax > water_level) vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; else if (flooded && full_ymax < water_level) vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode; else vm->m_data[i] = airnode; } else { if (c == CONTENT_IGNORE) continue; vm->m_data[i] = airnode; vm->m_flags[i] |= VMANIP_FLAG_CAVE; } } } } } inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p) { if (heightmap != NULL && p.Z >= node_min.Z && p.Z <= node_max.Z && p.X >= node_min.X && p.X <= node_max.X) { u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); if (heightmap[index] < p.Y) return true; } else if (p.Y > water_level) { return true; } return false; } //// //// CavesV6 //// CavesV6::CavesV6(const NodeDefManager *ndef, GenerateNotifier *gennotify, int water_level, content_t water_source, content_t lava_source) { assert(ndef); this->ndef = ndef; this->gennotify = gennotify; this->water_level = water_level; c_water_source = water_source; if (c_water_source == CONTENT_IGNORE) c_water_source = ndef->getId("mapgen_water_source"); if (c_water_source == CONTENT_IGNORE) c_water_source = CONTENT_AIR; c_lava_source = lava_source; if (c_lava_source == CONTENT_IGNORE) c_lava_source = ndef->getId("mapgen_lava_source"); if (c_lava_source == CONTENT_IGNORE) c_lava_source = CONTENT_AIR; } void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave, int max_stone_height, s16 *heightmap) { assert(vm); assert(ps); assert(ps2); this->vm = vm; this->ps = ps; this->ps2 = ps2; this->node_min = nmin; this->node_max = nmax; this->heightmap = heightmap; this->large_cave = is_large_cave; this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); int dswitchint = ps->range(1, 14); if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); min_tunnel_diameter = 5; max_tunnel_diameter = ps->range(7, ps->range(8, 24)); } else { part_max_length_rs = ps->range(2, 9); tunnel_routepoints = ps->range(10, ps->range(15, 30)); } large_cave_is_flat = (ps->range(0, 1) == 0); main_direction = v3f(0, 0, 0); // Allowed route area size in nodes ar = node_max - node_min + v3s16(1, 1, 1); // Area starting point in nodes of = node_min; // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 max_spread_amount = MAP_BLOCKSIZE; const s16 insure = 10; s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); if (large_cave) { s16 minpos = 0; if (node_min.Y < water_level && node_max.Y > water_level) { minpos = water_level - max_tunnel_diameter / 3 - of.Y; route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; } route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position orp.Z = (float)(ps->next() % ar.Z) + 0.5f; orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify != NULL) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; gennotify->addEvent(notifytype, abs_pos); } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event if (gennotify != NULL) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); } } void CavesV6::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; main_direction *= (float)ps->range(0, 10) / 10; } // Randomize size s16 min_d = min_tunnel_diameter; s16 max_d = max_tunnel_diameter; rs = ps->range(min_d, max_d); s16 rs_part_max_length_rs = rs * part_max_length_rs; v3s16 maxlen; if (large_cave) { maxlen = v3s16( rs_part_max_length_rs, rs_part_max_length_rs / 2, rs_part_max_length_rs ); } else { maxlen = v3s16( rs_part_max_length_rs, ps->range(1, rs_part_max_length_rs), rs_part_max_length_rs ); } v3f vec; vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2; vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; } // Do not make caves that are entirely above ground, to fix shadow bugs // caused by overgenerated large caves. // It is only necessary to check the startpoint and endpoint. v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; // If startpoint and endpoint are above ground, disable placement of nodes // in carveRoute while still running all PseudoRandom calls to ensure caves // are consistent with existing worlds. bool tunnel_above_ground = p1.Y > getSurfaceFromHeightmap(p1) && p2.Y > getSurfaceFromHeightmap(p2); vec += main_direction; v3f rp = orp + vec; if (rp.X < 0) rp.X = 0; else if (rp.X >= ar.X) rp.X = ar.X - 1; if (rp.Y < route_y_min) rp.Y = route_y_min; else if (rp.Y >= route_y_max) rp.Y = route_y_max - 1; if (rp.Z < 0) rp.Z = 0; else if (rp.Z >= ar.Z) rp.Z = ar.Z - 1; vec = rp - orp; float veclen = vec.getLength(); // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE if (veclen < 0.05f) veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps2->range(1, 2) == 1); // Carve routes for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz, tunnel_above_ground); orp = rp; } void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); MapNode lavanode(c_lava_source); v3s16 startp(orp.X, orp.Y, orp.Z); startp += of; v3f fp = orp + vec * f; fp.X += 0.1f * ps->range(-10, 10); fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); d1 += ps->range(-1, 1); } for (s16 z0 = d0; z0 <= d1; z0++) { s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { if (tunnel_above_ground) continue; s16 maxabsxz = MYMAX(abs(x0), abs(z0)); s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); for (s16 y0 = -si2; y0 <= si2; y0++) { if (large_cave_is_flat) { // Make large caves not so tall if (rs > 7 && abs(y0) >= rs / 3) continue; } v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); p += of; if (!vm->m_area.contains(p)) continue; u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (!ndef->get(c).is_ground_content) continue; if (large_cave) { int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; if (full_ymin < water_level && full_ymax > water_level) { vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; } else if (full_ymax < water_level) { vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode; } else { vm->m_data[i] = airnode; } } else { if (c == CONTENT_IGNORE || c == CONTENT_AIR) continue; vm->m_data[i] = airnode; vm->m_flags[i] |= VMANIP_FLAG_CAVE; } } } } } inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p) { if (heightmap != NULL && p.Z >= node_min.Z && p.Z <= node_max.Z && p.X >= node_min.X && p.X <= node_max.X) { u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); return heightmap[index]; } return water_level; }