aboutsummaryrefslogtreecommitdiff
path: root/src/filesys.h
blob: a26fe28d6f21f41c795e3118e20a20e7c2b08dd7 (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
/*
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.
*/

#pragma once

#include <set>
#include <string>
#include <vector>
#include "exceptions.h"

#ifdef _WIN32
#define DIR_DELIM "\\"
#define DIR_DELIM_CHAR '\\'
#define FILESYS_CASE_INSENSITIVE true
#define PATH_DELIM ";"
#else
#define DIR_DELIM "/"
#define DIR_DELIM_CHAR '/'
#define FILESYS_CASE_INSENSITIVE false
#define PATH_DELIM ":"
#endif

namespace irr { namespace io {
class IFileSystem;
}}

namespace fs
{

struct DirListNode
{
	std::string name;
	bool dir;
};

std::vector<DirListNode> GetDirListing(const std::string &path);

// Returns true if already exists
bool CreateDir(const std::string &path);

bool PathExists(const std::string &path);

bool IsPathAbsolute(const std::string &path);

bool IsDir(const std::string &path);

inline bool IsFile(const std::string &path)
{
	return PathExists(path) && !IsDir(path);
}

bool IsDirDelimiter(char c);

// Only pass full paths to this one. True on success.
// NOTE: The WIN32 version returns always true.
bool RecursiveDelete(const std::string &path);

bool DeleteSingleFileOrEmptyDirectory(const std::string &path);

// Returns path to temp directory, can return "" on error
std::string TempPath();

// Returns path to securely-created temporary file (will already exist when this function returns)
// can return "" on error
std::string CreateTempFile();

/* Returns a list of subdirectories, including the path itself, but excluding
       hidden directories (whose names start with . or _)
*/
void GetRecursiveDirs(std::vector<std::string> &dirs, const std::string &dir);
std::vector<std::string> GetRecursiveDirs(const std::string &dir);

/* Multiplatform */

/* The path itself not included, returns a list of all subpaths.
   dst - vector that contains all the subpaths.
   list files - include files in the list of subpaths.
   ignore - paths that start with these charcters will not be listed.
*/
void GetRecursiveSubPaths(const std::string &path,
		  std::vector<std::string> &dst,
		  bool list_files,
		  const std::set<char> &ignore = {});

// Only pass full paths to this one. True on success.
bool RecursiveDeleteContent(const std::string &path);

// Create all directories on the given path that don't already exist.
bool CreateAllDirs(const std::string &path);

// Copy a regular file
bool CopyFileContents(const std::string &source, const std::string &target);

// Copy directory and all subdirectories
// Omits files and subdirectories that start with a period
bool CopyDir(const std::string &source, const std::string &target);

// Move directory and all subdirectories
// Behavior with files/subdirs that start with a period is undefined
bool MoveDir(const std::string &source, const std::string &target);

// Check if one path is prefix of another
// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
// Ignores case differences and '/' vs. '\\' on Windows
bool PathStartsWith(const std::string &path, const std::string &prefix);

// Remove last path component and the dir delimiter before and/or after it,
// returns "" if there is only one path component.
// removed: If non-NULL, receives the removed component(s).
// count: Number of components to remove
std::string RemoveLastPathComponent(const std::string &path,
               std::string *removed = NULL, int count = 1);

// Remove "." and ".." path components and for every ".." removed, remove
// the last normal path component before it. Unlike AbsolutePath,
// this does not resolve symlinks and check for existence of directories.
std::string RemoveRelativePathComponents(std::string path);

// Returns the absolute path for the passed path, with "." and ".." path
// components and symlinks removed.  Returns "" on error.
std::string AbsolutePath(const std::string &path);

// Returns the filename from a path or the entire path if no directory
// delimiter is found.
const char *GetFilenameFromPath(const char *path);

bool safeWriteToFile(const std::string &path, const std::string &content);

#ifndef SERVER
bool extractZipFile(irr::io::IFileSystem *fs, const char *filename, const std::string &destination);
#endif

bool ReadFile(const std::string &path, std::string &out);

bool Rename(const std::string &from, const std::string &to);

} // namespace fs
l 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 "dungeongen.h" #include "mapgen.h" #include "voxel.h" #include "noise.h" #include "mapblock.h" #include "mapnode.h" #include "map.h" #include "nodedef.h" #include "profiler.h" #include "settings.h" // For g_settings #include "main.h" // For g_profiler //#define DGEN_USE_TORCHES NoiseParams nparams_dungeon_rarity(0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8); NoiseParams nparams_dungeon_wetness(0.0, 1.0, v3f(40.0, 40.0, 40.0), 32474, 4, 1.1); NoiseParams nparams_dungeon_density(0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4); /////////////////////////////////////////////////////////////////////////////// DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) { this->mg = mapgen; this->vm = mapgen->vm; #ifdef DGEN_USE_TORCHES c_torch = ndef->getId("default:torch"); #endif if (dparams) { memcpy(&dp, dparams, sizeof(dp)); } else { dp.c_water = mg->ndef->getId("mapgen_water_source"); dp.c_cobble = mg->ndef->getId("mapgen_cobble"); dp.c_moss = mg->ndef->getId("mapgen_mossycobble"); dp.c_stair = mg->ndef->getId("mapgen_stair_cobble"); dp.diagonal_dirs = false; dp.mossratio = 3.0; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0,0,0); dp.notifytype = GENNOTIFY_DUNGEON; dp.np_rarity = nparams_dungeon_rarity; dp.np_wetness = nparams_dungeon_wetness; dp.np_density = nparams_dungeon_density; } } void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) { //TimeTaker t("gen dungeons"); int approx_groundlevel = 10 + mg->water_level; if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel || NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mg->seed) < 0.2) return; this->blockseed = bseed; random.seed(bseed + 2); // Dungeon generator doesn't modify places which have this set vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE); // Set all air and water to be untouchable to make dungeons open // to caves and open air for (s16 z = nmin.Z; z <= nmax.Z; z++) { for (s16 y = nmin.Y; y <= nmax.Y; y++) { u32 i = vm->m_area.index(nmin.X, y, z); for (s16 x = nmin.X; x <= nmax.X; x++) { content_t c = vm->m_data[i].getContent(); if (c == CONTENT_AIR || c == dp.c_water) vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; i++; } } } // Add it makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE); // Convert some cobble to mossy cobble if (dp.mossratio != 0.0) { for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 y = nmin.Y; y <= nmax.Y; y++) { u32 i = vm->m_area.index(nmin.X, y, z); for (s16 x = nmin.X; x <= nmax.X; x++) { if (vm->m_data[i].getContent() == dp.c_cobble) { float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, mg->seed); float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed); if (density < wetness / dp.mossratio) vm->m_data[i].setContent(dp.c_moss); } i++; } } } //printf("== gen dungeons: %dms\n", t.stop()); } void DungeonGen::makeDungeon(v3s16 start_padding) { v3s16 areasize = vm->m_area.getExtent(); v3s16 roomsize; v3s16 roomplace; /* Find place for first room */ bool fits = false; for (u32 i = 0; i < 100 && !fits; i++) { bool is_large_room = ((random.next() & 3) == 1); roomsize = is_large_room ? v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) : v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8)); roomsize += dp.roomsize; // start_padding is used to disallow starting the generation of // a dungeon in a neighboring generation chunk roomplace = vm->m_area.MinEdge + start_padding + v3s16( random.range(0,areasize.X-roomsize.X-1-start_padding.X), random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y), random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z)); /* Check that we're not putting the room to an unknown place, otherwise it might end up floating in the air */ fits = true; for (s16 z = 1; z < roomsize.Z - 1; z++) for (s16 y = 1; y < roomsize.Y - 1; y++) for (s16 x = 1; x < roomsize.X - 1; x++) { v3s16 p = roomplace + v3s16(x, y, z); u32 vi = vm->m_area.index(p); if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) || vm->m_data[vi].getContent() == CONTENT_IGNORE) { fits = false; break; } } } // No place found if (fits == false) return; /* Stores the center position of the last room made, so that a new corridor can be started from the last room instead of the new room, if chosen so. */ v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); u32 room_count = random.range(2, 16); for (u32 i = 0; i < room_count; i++) { // Make a room to the determined place makeRoom(roomsize, roomplace); v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); if (mg->gennotify & (1 << dp.notifytype)) { std::vector <v3s16> *nvec = mg->gen_notifications[dp.notifytype]; nvec->push_back(room_center); } #ifdef DGEN_USE_TORCHES // Place torch at room center (for testing) vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch); #endif // Quit if last room if (i == room_count - 1) break; // Determine walker start position bool start_in_last_room = (random.range(0, 2) != 0); v3s16 walker_start_place; if (start_in_last_room) { walker_start_place = last_room_center; } else { walker_start_place = room_center; // Store center of current room as the last one last_room_center = room_center; } // Create walker and find a place for a door v3s16 doorplace; v3s16 doordir; m_pos = walker_start_place; if (!findPlaceForDoor(doorplace, doordir)) return; if (random.range(0,1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door doorplace -= doordir; // Make a random corridor starting from the door v3s16 corridor_end; v3s16 corridor_end_dir; makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); // Find a place for a random sized room roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); roomsize += dp.roomsize; m_pos = corridor_end; m_dir = corridor_end_dir; if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace)) return; if (random.range(0,1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door roomplace -= doordir; } } void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) { MapNode n_cobble(dp.c_cobble); MapNode n_air(CONTENT_AIR); // Make +-X walls for (s16 z = 0; z < roomsize.Z; z++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(0, y, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Make +-Z walls for (s16 x = 0; x < roomsize.X; x++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(x, y, 0); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Make +-Y walls (floor and ceiling) for (s16 z = 0; z < roomsize.Z; z++) for (s16 x = 0; x < roomsize.X; x++) { { v3s16 p = roomplace + v3s16(x, 0, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } { v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_cobble; } } // Fill with air for (s16 z = 1; z < roomsize.Z - 1; z++) for (s16 y = 1; y < roomsize.Y - 1; y++) for (s16 x = 1; x < roomsize.X - 1; x++) { v3s16 p = roomplace + v3s16(x, y, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; vm->m_data[vi] = n_air; } } void DungeonGen::makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags) { for (s16 z = 0; z < size.Z; z++) for (s16 y = 0; y < size.Y; y++) for (s16 x = 0; x < size.X; x++) { v3s16 p = place + v3s16(x, y, z); if (vm->m_area.contains(p) == false) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & avoid_flags) continue; vm->m_flags[vi] |= or_flags; vm->m_data[vi] = n; } } void DungeonGen::makeHole(v3s16 place) { makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE); } void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir) { makeHole(doorplace); #ifdef DGEN_USE_TORCHES // Place torch (for testing) vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch); #endif } void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, v3s16 &result_place, v3s16 &result_dir) { makeHole(doorplace); v3s16 p0 = doorplace; v3s16 dir = doordir; u32 length; /*if (random.next() % 2) length = random.range(1, 13); else length = random.range(1, 6);*/ length = random.range(1, 13); u32 partlength = random.range(1, 13); u32 partcount = 0; s16 make_stairs = 0; if (random.next() % 2 == 0 && partlength >= 3) make_stairs = random.next() % 2 ? 1 : -1; for (u32 i = 0; i < length; i++) { v3s16 p = p0 + dir; if (partcount != 0) p.Y += make_stairs; if (vm->m_area.contains(p) == true && vm->m_area.contains(p + v3s16(0, 1, 0)) == true) { if (make_stairs) { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0); makeHole(p); makeHole(p - dir); // TODO: fix stairs code so it works 100% (quite difficult) // exclude stairs from the bottom step // exclude stairs from diagonal steps if (((dir.X ^ dir.Z) & 1) && (((make_stairs == 1) && i != 0) || ((make_stairs == -1) && i != length - 1))) { // rotate face 180 deg if making stairs backwards int facedir = dir_to_facedir(dir * make_stairs); u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z); if (vm->m_data[vi].getContent() == dp.c_cobble) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); vi = vm->m_area.index(p.X, p.Y, p.Z); if (vm->m_data[vi].getContent() == dp.c_cobble) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); } } else { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0); makeHole(p); } p0 = p; } else { // Can't go here, turn away dir = turn_xz(dir, random.range(0, 1)); make_stairs = -make_stairs; partcount = 0; partlength = random.range(1, length); continue; } partcount++; if (partcount >= partlength) { partcount = 0; dir = random_turn(random, dir); partlength = random.range(1,length);