本篇内容从玩家点击“创建世界”开始,到具体的生成区块前为止

世界生成过程

世界设置
首先从创建世界的GUI开始看,类名net.minecraft.client.gui.GuiCreateWorld,以下是点击按钮的代码
/** * Called by the controls from the buttonList when activated. (Mouse pressed for buttons) */ protected void actionPerformed(GuiButton button) throws IOException { // ... else if (button.id == 0) // 判断点击的是不是“创建新的世界”按钮 { this.mc.displayGuiScreen((GuiScreen)null); if (this.field_146345_x) // 已经点击了这个按钮,不再重复以下代码 { return; } this.field_146345_x = true; // 这个i就是随机数种子,初始化为随机数 long i = (new Random()).nextLong(); // s是玩家输入的种子 String s = this.field_146335_h.getText(); // 如果玩家输入了种子,使用玩家输入的种子 if (!StringUtils.isEmpty(s)) { try { long j = Long.parseLong(s); if (j != 0L) { i = j; } } catch (NumberFormatException var7) { // 如果输入的不是数字,使用字符串的哈希值 i = (long)s.hashCode(); } } WorldType.worldTypes[this.selectedIndex].onGUICreateWorldPress(); // 目前版本这个函数为空 // 游戏模式(生存、创造等),这个决定玩家的能力(飞行、无敌等) WorldSettings.GameType worldsettings$gametype = WorldSettings.GameType.getByName(this.gameMode); // 这里传入了各种游戏设置,就是你在GUI上看到的那些设置(生成建筑、超平坦等) WorldSettings worldsettings = new WorldSettings(i, worldsettings$gametype, this.field_146341_s, this.field_146337_w, WorldType.worldTypes[this.selectedIndex]); worldsettings.setWorldName(this.chunkProviderSettingsJson); // 世界名称 if (this.field_146338_v && !this.field_146337_w) { worldsettings.enableBonusChest(); // 奖励箱 } if (this.allowCheats && !this.field_146337_w) { worldsettings.enableCommands(); // 允许作弊 } // 启动游戏服务器,参数为存档文件夹路径、世界(文件夹)名、游戏设置 this.mc.launchIntegratedServer(this.field_146336_i, this.field_146333_g.getText().trim(), worldsettings); } // ...
启动游戏服务器
这里补充一些小知识,MC的单人游戏其实是在本进程内建了服务器,服务器才是负责生成世界的,客户端只负责操作输入、渲染等
类名net.minecraft.client.Minecraft
/** * Arguments: World foldername, World ingame name, WorldSettings */ public void launchIntegratedServer(String folderName, String worldName, WorldSettings worldSettingsIn) { net.minecraftforge.fml.client.FMLClientHandler.instance().startIntegratedServer(folderName, worldName, worldSettingsIn); // 首先卸载已经加载的世界 this.loadWorld((WorldClient)null); System.gc(); ISaveHandler isavehandler = this.saveLoader.getSaveLoader(folderName, false); // 世界信息,包含世界名、难度、时间、出生点等东西 WorldInfo worldinfo = isavehandler.loadWorldInfo(); // 因为是创造新的世界,这里worldinfo == null if (worldinfo == null && worldSettingsIn != null) { worldinfo = new WorldInfo(worldSettingsIn, folderName); isavehandler.saveWorldInfo(worldinfo); } if (worldSettingsIn == null) { worldSettingsIn = new WorldSettings(worldinfo); } try { // 启动游戏服务器线程 this.theIntegratedServer = new IntegratedServer(this, folderName, worldName, worldSettingsIn); this.theIntegratedServer.startServerThread(); this.integratedServerIsRunning = true; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Starting integrated server"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Starting integrated server"); crashreportcategory.addCrashSection("Level ID", folderName); crashreportcategory.addCrashSection("Level Name", worldName); throw new ReportedException(crashreport); } this.loadingScreen.displaySavingString(I18n.format("menu.loadingLevel", new Object[0])); // 轮询获取加载世界的进度并显示 while (!this.theIntegratedServer.serverIsInRunLoop()) { if (!net.minecraftforge.fml.common.StartupQuery.check()) { loadWorld(null); displayGuiScreen(null); return; } String s = this.theIntegratedServer.getUserMessage(); if (s != null) { this.loadingScreen.displayLoadingString(I18n.format(s, new Object[0])); } else { this.loadingScreen.displayLoadingString(""); } try { Thread.sleep(200L); } catch (InterruptedException var9) { ; } } // 以下是客户端与服务器连接,这里不讨论 this.displayGuiScreen((GuiScreen)null); SocketAddress socketaddress = this.theIntegratedServer.getNetworkSystem().addLocalEndpoint(); NetworkManager networkmanager = NetworkManager.provideLocalClient(socketaddress); networkmanager.setNetHandler(new NetHandlerLoginClient(networkmanager, this, (GuiScreen)null)); networkmanager.sendPacket(new C00Handshake(47, socketaddress.toString(), 0, EnumConnectionState.LOGIN, true)); com.mojang.authlib.GameProfile gameProfile = this.getSession().getProfile(); if (!this.getSession().hasCachedProperties()) { gameProfile = sessionService.fillProfileProperties(gameProfile, true); //Forge: Fill profile properties upon game load. Fixes MC-52974. this.getSession().setProperties(gameProfile.getProperties()); } networkmanager.sendPacket(new C00PacketLoginStart(gameProfile)); this.myNetworkManager = networkmanager; }
加载世界
类名net.minecraft.server.integrated.IntegratedServer
服务器线程启动后调用这个函数
/** * Initialises the server and starts it. */ protected boolean startServer() throws IOException { logger.info("Starting integrated minecraft server version 1.8.9"); this.setOnlineMode(true); this.setCanSpawnAnimals(true); this.setCanSpawnNPCs(true); this.setAllowPvp(true); this.setAllowFlight(true); logger.info("Generating keypair"); this.setKeyPair(CryptManager.generateKeyPair()); if (!net.minecraftforge.fml.common.FMLCommonHandler.instance().handleServerAboutToStart(this)) return false; // 加载所有世界(主世界、地狱、末地等) this.loadAllWorlds(this.getFolderName(), this.getWorldName(), this.theWorldSettings.getSeed(), this.theWorldSettings.getTerrainType(), this.theWorldSettings.getWorldName()); this.setMOTD(this.getServerOwner() + " - " + this.worldServers[0].getWorldInfo().getWorldName()); return net.minecraftforge.fml.common.FMLCommonHandler.instance().handleServerStarting(this); }
protected void loadAllWorlds(String folderName, String worldName, long seed, WorldType type, String p_71247_6_) { this.convertMapIfNeeded(folderName); ISaveHandler isavehandler = this.getActiveAnvilConverter().getSaveLoader(folderName, true); this.setResourcePackFromWorld(this.getFolderName(), isavehandler); WorldInfo worldinfo = isavehandler.loadWorldInfo(); if (worldinfo == null) { worldinfo = new WorldInfo(this.theWorldSettings, worldName); } else { worldinfo.setWorldName(worldName); } WorldServer overWorld = (isDemo() ? (WorldServer)(new DemoWorldServer(this, isavehandler, worldinfo, 0, this.theProfiler)).init() : (WorldServer)(new WorldServer(this, isavehandler, worldinfo, 0, this.theProfiler)).init()); overWorld.initialize(this.theWorldSettings); // 初始化各世界 for (int dim : net.minecraftforge.common.DimensionManager.getStaticDimensionIDs()) { WorldServer world = (dim == 0 ? overWorld : (WorldServer)(new WorldServerMulti(this, isavehandler, dim, overWorld, this.theProfiler)).init()); world.addWorldAccess(new WorldManager(this, world)); if (!this.isSinglePlayer()) { world.getWorldInfo().setGameType(getGameType()); } net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.WorldEvent.Load(world)); } this.getConfigurationManager().setPlayerManager(new WorldServer[]{ overWorld }); if (overWorld.getWorldInfo().getDifficulty() == null) { this.setDifficultyForAllWorlds(this.mc.gameSettings.difficulty); } // 加载玩家附近的区块 this.initialWorldChunkLoad(); }
类名net.minecraft.server.MinecraftServer
protected void initialWorldChunkLoad() { int i = 16; int j = 4; int k = 192; int l = 625; int i1 = 0; this.setUserMessage("menu.generatingTerrain"); int j1 = 0; logger.info("Preparing start region for level " + j1); WorldServer worldserver = net.minecraftforge.common.DimensionManager.getWorld(j1); BlockPos blockpos = worldserver.getSpawnPoint(); long k1 = getCurrentTimeMillis(); // MC中区块大小是16*16,所以这里是加载玩家附近25*25个区块 for (int l1 = -192; l1 <= 192 && this.isServerRunning(); l1 += 16) { for (int i2 = -192; i2 <= 192 && this.isServerRunning(); i2 += 16) { long j2 = getCurrentTimeMillis(); if (j2 - k1 > 1000L) { this.outputPercentRemaining("Preparing spawn area", i1 * 100 / 625); k1 = j2; } ++i1; // 加载一个区块,参数是区块坐标(方块坐标/16) worldserver.theChunkProviderServer.loadChunk(blockpos.getX() + l1 >> 4, blockpos.getZ() + i2 >> 4); } } this.clearCurrentTask(); }
类名net.minecraft.world.gen.ChunkProviderServer
/** * loads or generates the chunk at the chunk location specified */ public Chunk loadChunk(int p_73158_1_, int p_73158_2_) { return loadChunk(p_73158_1_, p_73158_2_, null); } public Chunk loadChunk(int par1, int par2, Runnable runnable) { long k = ChunkCoordIntPair.chunkXZ2Int(par1, par2); this.droppedChunksSet.remove(Long.valueOf(k)); Chunk chunk = (Chunk)this.id2ChunkMap.getValueByKey(k); net.minecraft.world.chunk.storage.AnvilChunkLoader loader = null; if (this.chunkLoader instanceof net.minecraft.world.chunk.storage.AnvilChunkLoader) { loader = (net.minecraft.world.chunk.storage.AnvilChunkLoader) this.chunkLoader; } // We can only use the queue for already generated chunks if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2)) { if (runnable != null) { net.minecraftforge.common.chunkio.ChunkIOExecutor.queueChunkLoad(this.worldObj, loader, this, par1, par2, runnable); return null; } else { chunk = net.minecraftforge.common.chunkio.ChunkIOExecutor.syncChunkLoad(this.worldObj, loader, this, par1, par2); } } else if (chunk == null) { // 此时区块还未生成所以会运行到这里 chunk = this.originalLoadChunk(par1, par2); } // If we didn't load the chunk async and have a callback run it now if (runnable != null) { runnable.run(); } return chunk; } public Chunk originalLoadChunk(int p_73158_1_, int p_73158_2_) { long i = ChunkCoordIntPair.chunkXZ2Int(p_73158_1_, p_73158_2_); this.droppedChunksSet.remove(Long.valueOf(i)); Chunk chunk = (Chunk)this.id2ChunkMap.getValueByKey(i); if (chunk == null) { boolean added = loadingChunks.add(i); if (!added) { net.minecraftforge.fml.common.FMLLog.bigWarning("There is an attempt to load a chunk (%d,%d) in di >mension %d that is already being loaded. This will cause weird chunk breakages.", p_73158_1_, p_73158_2_, worldObj.provider.getDimensionId()); } chunk = net.minecraftforge.common.ForgeChunkManager.fetchDormantChunk(i, this.worldObj); if (chunk == null) chunk = this.loadChunkFromFile(p_73158_1_, p_73158_2_); if (chunk == null) { if (this.serverChunkGenerator == null) { chunk = this.dummyChunk; } else { try { // 如果区块不存在,这个函数会生成区块 chunk = this.serverChunkGenerator.provideChunk(p_73158_1_, p_73158_2_); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception generating new chunk"); CrashReportCategory crashreportcategory = crashreport.makeCategory("Chunk to be generated"); crashreportcategory.addCrashSection("Location", String.format("%d,%d", new Object[] {Integer.valueOf(p_73158_1_), Integer.valueOf(p_73158_2_)})); crashreportcategory.addCrashSection("Position hash", Long.valueOf(i)); crashreportcategory.addCrashSection("Generator", this.serverChunkGenerator.makeString()); throw new ReportedException(crashreport); } } } this.id2ChunkMap.add(i, chunk); this.loadedChunks.add(chunk); loadingChunks.remove(i); chunk.onChunkLoad(); chunk.populateChunk(this, this, p_73158_1_, p_73158_2_); } return chunk; }
Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐