/* Minetest Copyright (C) 2010-2015 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 "mapgen.h" #include "voxel.h" #include "noise.h" #include "mapblock.h" #include "mapnode.h" #include "map.h" //#include "serverobject.h" #include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker #include "settings.h" // For g_settings #include "emerge.h" #include "dungeongen.h" #include "cavegen.h" #include "treegen.h" #include "mg_ore.h" #include "mg_decoration.h" #include "mapgen_v6.h" FlagDesc flagdesc_mapgen_v6[] = { {"jungles", MGV6_JUNGLES}, {"biomeblend", MGV6_BIOMEBLEND}, {"mudflow", MGV6_MUDFLOW}, {"snowbiomes", MGV6_SNOWBIOMES}, {"flat", MGV6_FLAT}, {"trees", MGV6_TREES}, {NULL, 0} }; ///////////////////////////////////////////////////////////////////////////// MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge) : Mapgen(mapgenid, params, emerge) { this->m_emerge = emerge; this->ystride = csize.X; //////fix this this->heightmap = new s16[csize.X * csize.Z]; MapgenV6Params *sp = (MapgenV6Params *)params->sparams; this->spflags = sp->spflags; this->freq_desert = sp->freq_desert; this->freq_beach = sp->freq_beach; np_cave = &sp->np_cave; np_humidity = &sp->np_humidity; np_trees = &sp->np_trees; np_apple_trees = &sp->np_apple_trees; //// Create noise objects noise_terrain_base = new Noise(&sp->np_terrain_base, seed, csize.X, csize.Y); noise_terrain_higher = new Noise(&sp->np_terrain_higher, seed, csize.X, csize.Y); noise_steepness = new Noise(&sp->np_steepness, seed, csize.X, csize.Y); noise_height_select = new Noise(&sp->np_height_select, seed, csize.X, csize.Y); noise_mud = new Noise(&sp->np_mud, seed, csize.X, csize.Y); noise_beach = new Noise(&sp->np_beach, seed, csize.X, csize.Y); noise_biome = new Noise(&sp->np_biome, seed, csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE); noise_humidity = new Noise(&sp->np_humidity, seed, csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE); //// Resolve nodes to be used INodeDefManager *ndef = emerge->ndef; c_stone = ndef->getId("mapgen_stone"); c_dirt = ndef->getId("mapgen_dirt"); c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass"); c_sand = ndef->getId("mapgen_sand"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); c_gravel = ndef->getId("mapgen_gravel"); c_desert_stone = ndef->getId("mapgen_desert_stone"); c_desert_sand = ndef->getId("mapgen_desert_sand"); c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow"); c_snow = ndef->getId("mapgen_snow"); c_snowblock = ndef->getId("mapgen_snowblock"); c_ice = ndef->getId("mapgen_ice"); c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); if (c_desert_sand == CONTENT_IGNORE) c_desert_sand = c_sand; if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) c_stair_cobble = c_cobble; if (c_dirt_with_snow == CONTENT_IGNORE) c_dirt_with_snow = c_dirt_with_grass; if (c_snow == CONTENT_IGNORE) c_snow = CONTENT_AIR; if (c_snowblock == CONTENT_IGNORE) c_snowblock = c_dirt_with_grass; if (c_ice == CONTENT_IGNORE) c_ice = c_water_source; } MapgenV6::~MapgenV6() { delete noise_terrain_base; delete noise_terrain_higher; delete noise_steepness; delete noise_height_select; delete noise_mud; delete noise_beach; delete noise_biome; delete noise_humidity; delete[] heightmap; } MapgenV6Params::MapgenV6Params() { spflags = MGV6_BIOMEBLEND | MGV6_MUDFLOW; freq_desert = 0.45; freq_beach = 0.15; np_terrain_base = NoiseParams(-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0); np_terrain_higher = NoiseParams(20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0); np_steepness = NoiseParams(0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0); np_height_select = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0); np_mud = NoiseParams(4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0); np_beach = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0); np_biome = NoiseParams(0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0); np_cave = NoiseParams(6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0); np_humidity = NoiseParams(0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0); np_trees = NoiseParams(0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0); np_apple_trees = NoiseParams(0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0); } void MapgenV6Params::readParams(const Settings *settings) { settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6); settings->getFloatNoEx("mgv6_freq_desert", freq_desert); settings->getFloatNoEx("mgv6_freq_beach", freq_beach); settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base); settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); settings->getNoiseParams("mgv6_np_steepness", np_steepness); settings->getNoiseParams("mgv6_np_height_select", np_height_select); settings->getNoiseParams("mgv6_np_mud", np_mud); settings->getNoiseParams("mgv6_np_beach", np_beach); settings->getNoiseParams("mgv6_np_biome", np_biome); settings->getNoiseParams("mgv6_np_cave", np_cave); settings->getNoiseParams("mgv6_np_humidity", np_humidity); settings->getNoiseParams("mgv6_np_trees", np_trees); settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees); } void MapgenV6Params::writeParams(Settings *settings) const { settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX); settings->setFloat("mgv6_freq_desert", freq_desert); settings->setFloat("mgv6_freq_beach", freq_beach); settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); settings->setNoiseParams("mgv6_np_steepness", np_steepness); settings->setNoiseParams("mgv6_np_height_select", np_height_select); settings->setNoiseParams("mgv6_np_mud", np_mud); settings->setNoiseParams("mgv6_np_beach", np_beach); settings->setNoiseParams("mgv6_np_biome", np_biome); settings->setNoiseParams("mgv6_np_cave", np_cave); settings->setNoiseParams("mgv6_np_humidity", np_humidity); settings->setNoiseParams("mgv6_np_trees", np_trees); settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees); } //////////////////////// Some helper functions for the map generator // Returns Y one under area minimum if not found s16 MapgenV6::find_stone_level(v2s16 p2d) { v3s16 em = vm->m_area.getExtent(); s16 y_nodes_max = vm->m_area.MaxEdge.Y; s16 y_nodes_min = vm->m_area.MinEdge.Y; u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); s16 y; for (y = y_nodes_max; y >= y_nodes_min; y--) { content_t c = vm->m_data[i].getContent(); if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone)) break; vm->m_area.add_y(em, i, -1); } return (y >= y_nodes_min) ? y : y_nodes_min - 1; } // Required by mapgen.h bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos) { /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level( seed, v2s16(blockpos.X, blockpos.Z));*/ // Nah, this is just a heuristic, just return something s16 minimum_groundlevel = water_level; if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel) return true; else return false; } //////////////////////// Base terrain height functions float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher, float steepness, float height_select) { float base = 1 + terrain_base; float higher = 1 + terrain_higher; // Limit higher ground level to at least base if(higher < base) higher = base; // Steepness factor of cliffs float b = steepness; b = rangelim(b, 0.0, 1000.0); b = 5 * b * b * b * b * b * b * b; b = rangelim(b, 0.5, 1000.0); // Values 1.5...100 give quite horrible looking slopes if (b > 1.5 && b < 100.0) b = (b < 10.0) ? 1.5 : 100.0; float a_off = -0.20; // Offset to more low float a = 0.5 + b * (a_off + height_select); a = rangelim(a, 0.0, 1.0); // Limit return base * (1.0 - a) + higher * a; } float MapgenV6::baseTerrainLevelFromNoise(v2s16 p) { if ((spflags & MGV6_FLAT) || (flags & MG_FLAT)) return water_level; float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np, p.X, 0.5, p.Y, 0.5, seed); float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np, p.X, 0.5, p.Y, 0.5, seed); float steepness = NoisePerlin2D_PO(&noise_steepness->np, p.X, 0.5, p.Y, 0.5, seed); float height_select = NoisePerlin2D_PO(&noise_height_select->np, p.X, 0.5, p.Y, 0.5, seed); return baseTerrainLevel(terrain_base, terrain_higher, steepness, height_select); } float MapgenV6::baseTerrainLevelFromMap(v2s16 p) { int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); return baseTerrainLevelFromMap(index); } float MapgenV6::baseTerrainLevelFromMap(int index) { if ((spflags & MGV6_FLAT) || (flags & MG_FLAT)) return water_level; float terrain_base = noise_terrain_base->result[index]; float terrain_higher = noise_terrain_higher->result[index]; float steepness = noise_steepness->result[index]; float height_select = noise_height_select->result[index]; return baseTerrainLevel(terrain_base, terrain_higher, steepness, height_select); } s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT; } int MapgenV6::getGroundLevelAtPoint(v2s16 p) { return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT; } int MapgenV6::getSpawnLevelAtPoint(v2s16 p) { s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT; if (level_at_point <= water_level || level_at_point > water_level + 16) return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point else return level_at_point; } //////////////////////// Noise functions float MapgenV6::getMudAmount(v2s16 p) { int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); return getMudAmount(index); } bool MapgenV6::getHaveBeach(v2s16 p) { int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); return getHaveBeach(index); } BiomeV6Type MapgenV6::getBiome(v2s16 p) { int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE) + (p.X - full_node_min.X); return getBiome(index, p); } float MapgenV6::getHumidity(v2s16 p) { /*double noise = noise2d_perlin( 0.5+(float)p.X/500, 0.5+(float)p.Y/500, seed+72384, 4, 0.66); noise = (noise + 1.0)/2.0;*/ int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE) + (p.X - full_node_min.X); float noise = noise_humidity->result[index]; if (noise < 0.0) noise = 0.0; if (noise > 1.0) noise = 1.0; return noise; } float MapgenV6::getTreeAmount(v2s16 p) { /*double noise = noise2d_perlin( 0.5+(float)p.X/125, 0.5+(float)p.Y/125, seed+2, 4, 0.66);*/ float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed); float zeroval = -0.39; if (noise < zeroval) return 0; else return 0.04 * (noise - zeroval) / (1.0 - zeroval); } bool MapgenV6::getHaveAppleTree(v2s16 p) { /*is_apple_tree = noise2d_perlin( 0.5+(float)p.X/10<span class="hl com">/*</span> <span class="hl com">Minetest</span> <span class="hl com">Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com></span> <span class="hl com"></span> <span class="hl com">This program is free software; you can redistribute it and/or modify</span> <span class="hl com">it under the terms of the GNU Lesser General Public License as published by</span> <span class="hl com">the Free Software Foundation; either version 2.1 of the License, or</span> <span class="hl com">(at your option) any later version.</span> <span class="hl com"></span> <span class="hl com">This program is distributed in the hope that it will be useful,</span> <span class="hl com">but WITHOUT ANY WARRANTY; without even the implied warranty of</span> <span class="hl com">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span> <span class="hl com">GNU Lesser General Public License for more details.</span> <span class="hl com"></span> <span class="hl com">You should have received a copy of the GNU Lesser General Public License along</span> <span class="hl com">with this program; if not, write to the Free Software Foundation, Inc.,</span> <span class="hl com">51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span> <span class="hl com">*/</span> <span class="hl ppc">#ifndef __ANDROID__</span> <span class="hl ppc">#error This file may only be compiled for android!</span> <span class="hl ppc">#endif</span> <span class="hl ppc">#include</span> <span class="hl pps">"porting.h"</span><span class="hl ppc"></span> <span class="hl ppc">#include</span> <span class="hl pps">"porting_android.h"</span><span class="hl ppc"></span> <span class="hl ppc">#include</span> <span class="hl pps">"config.h"</span><span class="hl ppc"></span> <span class="hl ppc">#include</span> <span class="hl pps">"filesys.h"</span><span class="hl ppc"></span> <span class="hl ppc">#include</span> <span class="hl pps">"log.h"</span><span class="hl ppc"></span> <span class="hl ppc">#include <sstream></span> <span class="hl ppc">#ifdef GPROF</span> <span class="hl ppc">#include</span> <span class="hl pps">"prof.h"</span><span class="hl ppc"></span> <span class="hl ppc">#endif</span> <span class="hl kwc">extern</span> <span class="hl kwb">int</span> <span class="hl kwd">main</span><span class="hl opt">(</span><span class="hl kwb">int</span> argc<span class="hl opt">,</span> <span class="hl kwb">char</span> <span class="hl opt">*</span>argv<span class="hl opt">[]);</span> <span class="hl kwb">void</span> <span class="hl kwd">android_main</span><span class="hl opt">(</span>android_app <span class="hl opt">*</span>app<span class="hl opt">)</span> <span class="hl opt">{</span> <span class="hl kwb">int</span> retval <span class="hl opt">=</span> <span class="hl num">0</span><span class="hl opt">;</span> <span class="hl kwc">porting</span><span class="hl opt">::</span>app_global <span class="hl opt">=</span> app<span class="hl opt">;</span> <span class="hl kwc">porting</span><span class="hl opt">::</span><span class="hl kwd">setThreadName</span><span class="hl opt">(</span><span class="hl str">"MainThread"</span><span class="hl opt">);</span> <span class="hl kwa">try</span> <span class="hl opt">{</span> <span class="hl kwd">app_dummy</span><span class="hl opt">();</span> <span class="hl kwb">char</span> <span class="hl opt">*</span>argv<span class="hl opt">[] = { (</span><span class="hl kwb">char</span><span class="hl opt">*)</span> <span class="hl str">"minetest"</span> <span class="hl opt">};</span> <span class="hl kwd">main</span><span class="hl opt">(</span><span class="hl kwa">sizeof</span><span class="hl opt">(</span>argv<span class="hl opt">) /</span> <span class="hl kwa">sizeof</span><span class="hl opt">(</span>argv<span class="hl opt">[</span><span class="hl num">0</span><span class="hl opt">]),</span> argv<span class="hl opt">);</span> <span class="hl opt">}</span> <span class="hl kwa">catch</span><span class="hl opt">(</span>BaseException e<span class="hl opt">) {</span> <span class="hl kwc">std</span><span class="hl opt">::</span>stringstream msg<span class="hl opt">;</span> msg <span class="hl opt"><<</span> <span class="hl str">"Exception handled by main: "</span> <span class="hl opt"><<</span> e<span class="hl opt">.</span><span class="hl kwd">what</span><span class="hl opt">();</span> <span class="hl kwb">const char</span><span class="hl opt">*</span> message <span class="hl opt">=</span> msg<span class="hl opt">.</span><span class="hl kwd">str</span><span class="hl opt">().</span><span class="hl kwd">c_str</span><span class="hl opt">();</span> <span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span> <span class="hl str">"%s"</span><span class="hl opt">,</span> message<span class="hl opt">);</span> errorstream <span class="hl opt"><<</span> msg <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> retval <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">;</span> <span class="hl opt">}</span> <span class="hl kwa">catch</span><span class="hl opt">(...) {</span> <span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span> <span class="hl str">"Some exception occured"</span><span class="hl opt">);</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"Uncaught exception in main thread!"</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> retval <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">;</span> <span class="hl opt">}</span> <span class="hl kwc">porting</span><span class="hl opt">::</span><span class="hl kwd">cleanupAndroid</span><span class="hl opt">();</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"Shutting down minetest."</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl kwd">exit</span><span class="hl opt">(</span>retval<span class="hl opt">);</span> <span class="hl opt">}</span> <span class="hl com">/* handler for finished message box input */</span> <span class="hl com">/* Intentionally NOT in namespace porting */</span> <span class="hl com">/* TODO this doesn't work as expected, no idea why but there's a workaround */</span> <span class="hl com">/* for it right now */</span> <span class="hl kwc">extern</span> <span class="hl str">"C"</span> <span class="hl opt">{</span> JNIEXPORT <span class="hl kwb">void</span> JNICALL <span class="hl kwd">Java_net_minetest_MtNativeActivity_putMessageBoxResult</span><span class="hl opt">(</span> JNIEnv <span class="hl opt">*</span> env<span class="hl opt">,</span> jclass thiz<span class="hl opt">,</span> jstring text<span class="hl opt">)</span> <span class="hl opt">{</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"Java_net_minetest_MtNativeActivity_putMessageBoxResult got: "</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span><span class="hl kwd">string</span><span class="hl opt">((</span><span class="hl kwb">const char</span><span class="hl opt">*)</span>env<span class="hl opt">-></span><span class="hl kwd">GetStringChars</span><span class="hl opt">(</span>text<span class="hl opt">,</span><span class="hl num">0</span><span class="hl opt">))</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl opt">}</span> <span class="hl opt">}</span> <span class="hl kwa">namespace</span> porting <span class="hl opt">{</span> <span class="hl kwc">std</span><span class="hl opt">::</span>string path_storage <span class="hl opt">=</span> DIR_DELIM <span class="hl str">"sdcard"</span> DIR_DELIM<span class="hl opt">;</span> android_app<span class="hl opt">*</span> app_global<span class="hl opt">;</span> JNIEnv<span class="hl opt">*</span> jnienv<span class="hl opt">;</span> jclass nativeActivity<span class="hl opt">;</span> jclass <span class="hl kwd">findClass</span><span class="hl opt">(</span><span class="hl kwc">std</span><span class="hl opt">::</span>string classname<span class="hl opt">)</span> <span class="hl opt">{</span> <span class="hl kwa">if</span> <span class="hl opt">(</span>jnienv <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span> <span class="hl kwa">return</span> <span class="hl num">0</span><span class="hl opt">;</span> <span class="hl opt">}</span> jclass nativeactivity <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">"android/app/NativeActivity"</span><span class="hl opt">);</span> jmethodID getClassLoader <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>nativeactivity<span class="hl opt">,</span><span class="hl str">"getClassLoader"</span><span class="hl opt">,</span> <span class="hl str">"()Ljava/lang/ClassLoader;"</span><span class="hl opt">);</span> jobject cls <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">CallObjectMethod</span><span class="hl opt">(</span>app_global<span class="hl opt">-></span>activity<span class="hl opt">-></span>clazz<span class="hl opt">,</span> getClassLoader<span class="hl opt">);</span> jclass classLoader <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">"java/lang/ClassLoader"</span><span class="hl opt">);</span> jmethodID findClass <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>classLoader<span class="hl opt">,</span> <span class="hl str">"loadClass"</span><span class="hl opt">,</span> <span class="hl str">"(Ljava/lang/String;)Ljava/lang/Class;"</span><span class="hl opt">);</span> jstring strClassName <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">NewStringUTF</span><span class="hl opt">(</span>classname<span class="hl opt">.</span><span class="hl kwd">c_str</span><span class="hl opt">());</span> <span class="hl kwa">return</span> <span class="hl opt">(</span>jclass<span class="hl opt">)</span> jnienv<span class="hl opt">-></span><span class="hl kwd">CallObjectMethod</span><span class="hl opt">(</span>cls<span class="hl opt">,</span> findClass<span class="hl opt">,</span> strClassName<span class="hl opt">);</span> <span class="hl opt">}</span> <span class="hl kwb">void</span> <span class="hl kwd">copyAssets</span><span class="hl opt">()</span> <span class="hl opt">{</span> jmethodID assetcopy <span class="hl opt">=</span> jnienv<span class="hl opt">-></span><span class="hl kwd">GetMethodID</span><span class="hl opt">(</span>nativeActivity<span class="hl opt">,</span><span class="hl str">"copyAssets"</span><span class="hl opt">,</span><span class="hl str">"()V"</span><span class="hl opt">);</span> <span class="hl kwa">if</span> <span class="hl opt">(</span>assetcopy <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span> <span class="hl kwa">assert</span><span class="hl opt">(</span><span class="hl str">"porting::copyAssets unable to find copy assets method"</span> <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">);</span> <span class="hl opt">}</span> jnienv<span class="hl opt">-></span><span class="hl kwd">CallVoidMethod</span><span class="hl opt">(</span>app_global<span class="hl opt">-></span>activity<span class="hl opt">-></span>clazz<span class="hl opt">,</span> assetcopy<span class="hl opt">);</span> <span class="hl opt">}</span> <span class="hl kwb">void</span> <span class="hl kwd">initAndroid</span><span class="hl opt">()</span> <span class="hl opt">{</span> <span class="hl kwc">porting</span><span class="hl opt">::</span>jnienv <span class="hl opt">=</span> NULL<span class="hl opt">;</span> JavaVM <span class="hl opt">*</span>jvm <span class="hl opt">=</span> app_global<span class="hl opt">-></span>activity<span class="hl opt">-></span>vm<span class="hl opt">;</span> JavaVMAttachArgs lJavaVMAttachArgs<span class="hl opt">;</span> lJavaVMAttachArgs<span class="hl opt">.</span>version <span class="hl opt">=</span> JNI_VERSION_1_6<span class="hl opt">;</span> lJavaVMAttachArgs<span class="hl opt">.</span>name <span class="hl opt">=</span> <span class="hl str">"MinetestNativeThread"</span><span class="hl opt">;</span> lJavaVMAttachArgs<span class="hl opt">.</span>group <span class="hl opt">=</span> NULL<span class="hl opt">;</span> <span class="hl ppc">#ifdef NDEBUG</span> <span class="hl slc">// This is a ugly hack as arm v7a non debuggable builds crash without this</span> <span class="hl slc">// printf ... if someone finds out why please fix it!</span> infostream <span class="hl opt"><<</span> <span class="hl str">"Attaching native thread. "</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl ppc">#endif</span> <span class="hl kwa">if</span> <span class="hl opt">(</span> jvm<span class="hl opt">-></span><span class="hl kwd">AttachCurrentThread</span><span class="hl opt">(&</span><span class="hl kwc">porting</span><span class="hl opt">::</span>jnienv<span class="hl opt">, &</span>lJavaVMAttachArgs<span class="hl opt">) ==</span> JNI_ERR<span class="hl opt">) {</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"Failed to attach native thread to jvm"</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl kwd">exit</span><span class="hl opt">(-</span><span class="hl num">1</span><span class="hl opt">);</span> <span class="hl opt">}</span> nativeActivity <span class="hl opt">=</span> <span class="hl kwd">findClass</span><span class="hl opt">(</span><span class="hl str">"net/minetest/minetest/MtNativeActivity"</span><span class="hl opt">);</span> <span class="hl kwa">if</span> <span class="hl opt">(</span>nativeActivity <span class="hl opt">==</span> <span class="hl num">0</span><span class="hl opt">) {</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"porting::initAndroid unable to find java native activity class"</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl opt">}</span> <span class="hl ppc">#ifdef GPROF</span> <span class="hl com">/* in the start-up code */</span> <span class="hl kwd">__android_log_print</span><span class="hl opt">(</span>ANDROID_LOG_ERROR<span class="hl opt">,</span> PROJECT_NAME<span class="hl opt">,</span> <span class="hl str">"Initializing GPROF profiler"</span><span class="hl opt">);</span> <span class="hl kwd">monstartup</span><span class="hl opt">(</span><span class="hl str">"libminetest.so"</span><span class="hl opt">);</span> <span class="hl ppc">#endif</span> <span class="hl opt">}</span> <span class="hl kwb">void</span> <span class="hl kwd">cleanupAndroid</span><span class="hl opt">()</span> <span class="hl opt">{</span> <span class="hl ppc">#ifdef GPROF</span> errorstream <span class="hl opt"><<</span> <span class="hl str">"Shutting down GPROF profiler"</span> <span class="hl opt"><<</span> <span class="hl kwc">std</span><span class="hl opt">::</span>endl<span class="hl opt">;</span> <span class="hl kwd">setenv</span><span class="hl opt">(</span><span class="hl str">"CPUPROFILE"</span><span class="hl opt">, (</span>path_user <span class="hl opt">+</span> DIR_DELIM <span class="hl opt">+</span> <span class="hl str">"gmon.out"</span><span class="hl opt">).</span><span class="hl kwd">c_str</span><span class="hl opt">(),</span> <span class="hl num">1</span><span class="hl opt">);</span> <span class="hl kwd">moncleanup</span><span class="hl opt">();</span> <span class="hl ppc">#endif</span> JavaVM <span class="hl opt">*</span>jvm <span class="hl opt">=</span> app_global<span class="hl opt">-></span>activity<span class="hl opt">-></span>vm<span class="hl opt">;</span> jvm<span class="hl opt">-></span><span class="hl kwd">DetachCurrentThread</span><span class="hl opt">();</span> <span class="hl opt">}</span> <span class="hl kwb">void</span> <span class="hl kwd">setExternalStorageDir</span><span class="hl opt">(</span>JNIEnv<span class="hl opt">*</span> lJNIEnv<span class="hl opt">)</span> <span class="hl opt">{</span> <span class="hl slc">// Android: Retrieve ablsolute path to external storage device (sdcard)</span> jclass ClassEnv <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-></span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">"android/os/Environment"</span><span class="hl opt">);</span> jmethodID MethodDir <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-></span><span class="hl kwd">GetStaticMethodID</span><span class="hl opt">(</span>ClassEnv<span class="hl opt">,</span> <span class="hl str">"getExternalStorageDirectory"</span><span class="hl opt">,</span><span class="hl str">"()Ljava/io/File;"</span><span class="hl opt">);</span> jobject ObjectFile <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-></span><span class="hl kwd">CallStaticObjectMethod</span><span class="hl opt">(</span>ClassEnv<span class="hl opt">,</span> MethodDir<span class="hl opt">);</span> jclass ClassFile <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-></span><span class="hl kwd">FindClass</span><span class="hl opt">(</span><span class="hl str">"java/io/File"</span><span class="hl opt">);</span> jmethodID MethodPath <span class="hl opt">=</span> lJNIEnv<span class="hl opt">-></span><span class="hl kwd">GetMethodID</span><