├── .gitignore ├── resources ├── assets │ └── chickenchunks │ │ ├── textures │ │ ├── hedronmap.png │ │ ├── gui │ │ │ └── guiSmall.png │ │ └── blocks │ │ │ ├── block_0_0.png │ │ │ ├── block_0_1.png │ │ │ ├── block_0_2.png │ │ │ ├── block_1_0.png │ │ │ ├── block_1_1.png │ │ │ └── block_1_2.png │ │ └── lang │ │ ├── zh_TW.lang │ │ ├── ko_KR.lang │ │ ├── zh_CN.lang │ │ ├── ja_JP.lang │ │ ├── en_US.lang │ │ ├── cs_CZ.lang │ │ ├── it_IT.lang │ │ ├── de_DE.lang │ │ ├── ru_RU.lang │ │ ├── tr_TR.lang │ │ ├── pl_PL.lang │ │ ├── fr_CA.lang │ │ └── fr_FR.lang ├── mcmod.info └── chickenchunks_at.cfg ├── src └── codechicken │ └── chunkloader │ ├── IChickenChunkLoader.java │ ├── ItemChunkLoader.java │ ├── CommandDebugInfo.java │ ├── ChunkLoaderClientProxy.java │ ├── ChunkLoaderSPH.java │ ├── ChunkLoaderSBRH.java │ ├── CommandChunkLoaders.java │ ├── TileSpotLoader.java │ ├── ChickenChunks.java │ ├── ChunkLoaderProxy.java │ ├── ChunkLoaderCPH.java │ ├── ChunkLoaderEventHandler.java │ ├── TileChunkLoader.java │ ├── ChunkLoaderShape.java │ ├── GuiChunkLoader.java │ ├── TileChunkLoaderBase.java │ ├── BlockChunkLoader.java │ ├── PlayerChunkViewerTracker.java │ ├── PlayerChunkViewerManager.java │ ├── TileChunkLoaderRenderer.java │ ├── PlayerChunkViewer.java │ └── ChunkLoaderManager.java ├── README.md └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /build/.gradle 2 | /build/build 3 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/hedronmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/hedronmap.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/gui/guiSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/gui/guiSmall.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_0_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_0_0.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_0_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_0_1.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_0_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_0_2.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_1_0.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_1_1.png -------------------------------------------------------------------------------- /resources/assets/chickenchunks/textures/blocks/block_1_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chicken-Bones/ChickenChunks/HEAD/resources/assets/chickenchunks/textures/blocks/block_1_2.png -------------------------------------------------------------------------------- /resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "ChickenChunks", 4 | "name": "ChickenChunks", 5 | "description": "Advanced chunkloading management 6 | 7 | Credits: Sanguine - Texture 8 | 9 | Supporters:", 10 | "version": "${version}", 11 | "mcversion": "${mc_version}", 12 | "url": "http://www.minecraftforum.net/topic/909223", 13 | "authorList": [ "ChickenBones" ], 14 | "dependencies": [ "CodeChickenCore" ] 15 | } 16 | ] -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/zh_TW.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=區塊載入器 2 | tile.chickenChunkLoader|1.name=單點載入器 3 | 4 | chickenchunks.gui.name=區塊載入器 5 | chickenchunks.gui.showlasers=顯示鐳射 6 | chickenchunks.gui.hidelasers=隱藏鐳射 7 | chickenchunks.gui.chunk=%d 個區塊 8 | chickenchunks.gui.chunks=%d 個區塊 9 | chickenchunks.gui.radius=半徑 10 | 11 | chickenchunks.shape.square=方形 12 | chickenchunks.shape.circle=圓形 13 | chickenchunks.shape.linex=線形 X 14 | chickenchunks.shape.linez=線形 Z -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/ko_KR.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=청크로더 2 | tile.chickenChunkLoader|1.name=작은 청크로더 3 | 4 | chickenchunks.gui.name=청크로더 5 | chickenchunks.gui.showlasers=레이저 보기 6 | chickenchunks.gui.hidelasers=레이저 숨기기 7 | chickenchunks.gui.chunk=%d 청크 8 | chickenchunks.gui.chunks=%d 청크 9 | chickenchunks.gui.radius=반지름 10 | 11 | chickenchunks.shape.square=사각형 12 | chickenchunks.shape.circle=원 13 | chickenchunks.shape.linex=X라인 14 | chickenchunks.shape.linez=Z라인 15 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/IChickenChunkLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.Collection; 4 | 5 | import codechicken.lib.vec.BlockCoord; 6 | 7 | import net.minecraft.world.ChunkCoordIntPair; 8 | import net.minecraft.world.World; 9 | 10 | public abstract interface IChickenChunkLoader 11 | { 12 | public String getOwner(); 13 | public Object getMod(); 14 | public World getWorld(); 15 | public BlockCoord getPosition(); 16 | public void deactivate(); 17 | public void activate(); 18 | public Collection getChunks(); 19 | } 20 | -------------------------------------------------------------------------------- /resources/chickenchunks_at.cfg: -------------------------------------------------------------------------------- 1 | # ChickenChunks Access Transformer configuration file 2 | public net.minecraft.server.management.PlayerManager func_72690_a(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance; #getOrCreateChunkWatcher 3 | public net.minecraft.server.management.PlayerManager$PlayerInstance 4 | public net.minecraft.server.management.PlayerManager$PlayerInstance func_151251_a(Lnet/minecraft/network/Packet;)V #sendToAllPlayersWatchingChunk 5 | public net.minecraft.server.management.PlayerManager$PlayerInstance field_73263_b #playersWatchingChunk 6 | public net.minecraft.world.gen.ChunkProviderServer field_73245_g -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ItemChunkLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.item.ItemBlock; 5 | import net.minecraft.item.ItemStack; 6 | 7 | public class ItemChunkLoader extends ItemBlock 8 | { 9 | public ItemChunkLoader(Block block) { 10 | super(block); 11 | setHasSubtypes(true); 12 | } 13 | 14 | @Override 15 | public int getMetadata(int par1) { 16 | return par1; 17 | } 18 | 19 | @Override 20 | public String getUnlocalizedName(ItemStack stack) { 21 | return super.getUnlocalizedName() + "|" + stack.getItemDamage(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/zh_CN.lang: -------------------------------------------------------------------------------- 1 | #zh_CN 2 | 3 | tile.chickenChunkLoader|0.name=区块载入器 4 | tile.chickenChunkLoader|1.name=节点载入器 5 | 6 | chickenchunks.gui.name=区块载入器 7 | chickenchunks.gui.showlasers=显示镭射 8 | chickenchunks.gui.hidelasers=隐藏镭射 9 | chickenchunks.gui.chunk=%d个区块 10 | chickenchunks.gui.chunks=%d个区块 11 | chickenchunks.gui.radius=半径 12 | 13 | chickenchunks.shape.square=方形 14 | chickenchunks.shape.circle=圆形 15 | chickenchunks.shape.linex=X向线形 16 | chickenchunks.shape.linez=Z向线形 17 | 18 | chickenchunks.accessdenied=这个区块载入器不属于你. 19 | command.ccdebug=/ccdebug [dimension] 20 | command.chunkloaders=/chunkloaders 21 | command.chunkloaders.denied=你未被允许使用区块观察器. 22 | command.chunkloaders.alreadyopen=区块观察器已经开启. 23 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/ja_JP.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=チャンクローダー 2 | tile.chickenChunkLoader|1.name=スポットローダー 3 | 4 | chickenchunks.gui.name=チャンクローダー 5 | chickenchunks.gui.showlasers=レーザー:表示 6 | chickenchunks.gui.hidelasers=レーザー:非表示 7 | chickenchunks.gui.chunk=%d チャンク 8 | chickenchunks.gui.chunks=%d チャンク 9 | chickenchunks.gui.radius=範囲 10 | 11 | chickenchunks.shape.square=四角形 12 | chickenchunks.shape.circle=円形 13 | chickenchunks.shape.linex=X線上 14 | chickenchunks.shape.linez=Z線上 15 | 16 | chickenchunks.accessdenied=このチャンクローダーはあなたの管理下ではありません。 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=あなたはチャンクビューワを使用できません。 20 | command.chunkloaders.alreadyopen=チャンクビューワは既に開いています。 21 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/CommandDebugInfo.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.core.commands.CoreCommand; 4 | 5 | public class CommandDebugInfo extends CoreCommand 6 | { 7 | @Override 8 | public String getCommandName() { 9 | return "ccdebug"; 10 | } 11 | 12 | @Override 13 | public boolean OPOnly() { 14 | return false; 15 | } 16 | 17 | @Override 18 | public void handleCommand(String command, String playername, String[] args, WCommandSender listener) { 19 | 20 | } 21 | 22 | @Override 23 | public void printHelp(WCommandSender listener) { 24 | listener.chatT("command.ccdebug"); 25 | } 26 | 27 | @Override 28 | public int minimumParameters() { 29 | return 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/en_US.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Chunk Loader 2 | tile.chickenChunkLoader|1.name=Spot Loader 3 | 4 | chickenchunks.gui.name=Chunk Loader 5 | chickenchunks.gui.showlasers=Show Lasers 6 | chickenchunks.gui.hidelasers=Hide Lasers 7 | chickenchunks.gui.chunk=%d Chunk 8 | chickenchunks.gui.chunks=%d Chunks 9 | chickenchunks.gui.radius=Radius 10 | 11 | chickenchunks.shape.square=Square 12 | chickenchunks.shape.circle=Circle 13 | chickenchunks.shape.linex=Line X 14 | chickenchunks.shape.linez=Line Z 15 | 16 | chickenchunks.accessdenied=This Chunkloader does not belong to you. 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=You are not allowed to use the ChunkViewer. 20 | command.chunkloaders.alreadyopen=Chunk Viewer already open. -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/cs_CZ.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Načítač chunků 2 | tile.chickenChunkLoader|1.name=Načítač míst 3 | 4 | chickenchunks.gui.name=Načítač chunků 5 | chickenchunks.gui.showlasers=Zobrazit lasery 6 | chickenchunks.gui.hidelasers=Schovat lasery 7 | chickenchunks.gui.chunk=%d chunk 8 | chickenchunks.gui.chunks=%d chunků 9 | chickenchunks.gui.radius=Vzdálenost 10 | 11 | chickenchunks.shape.square=Čtverec 12 | chickenchunks.shape.circle=Kruh 13 | chickenchunks.shape.linex=Řada X 14 | chickenchunks.shape.linez=Řada Z 15 | 16 | chickenchunks.accessdenied=Tento načítač chunků ti nepatří. 17 | command.ccdebug=/ccdebug [dimenze] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Nejsi oprávněn používat Zobrazovač chunků. 20 | command.chunkloaders.alreadyopen=Zobrazovač chunků je již otevřený. -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/it_IT.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Carica-chunk 2 | tile.chickenChunkLoader|1.name=Carica-punto 3 | 4 | chickenchunks.gui.name=Carica-chunk 5 | chickenchunks.gui.showlasers=mostra lasers 6 | chickenchunks.gui.hidelasers=Nascondi lasers 7 | chickenchunks.gui.chunk=%d Chunk 8 | chickenchunks.gui.chunks=%d Chunks 9 | chickenchunks.gui.radius=Raggio 10 | 11 | chickenchunks.shape.square=Quadrato 12 | chickenchunks.shape.circle=Cerchio 13 | chickenchunks.shape.linex=Linea X 14 | chickenchunks.shape.linez=Linea Z 15 | 16 | chickenchunks.accessdenied=Questo Carica-chunk non ti appartiene. 17 | command.ccdebug=/ccdebug [dimensione] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Non hai il permesso di usare il ChunkViewer. 20 | command.chunkloaders.alreadyopen=ChunkViewer già aperto. 21 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/de_DE.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Chunkloader 2 | tile.chickenChunkLoader|1.name=Spotloader 3 | 4 | chickenchunks.gui.name=Chunkloader 5 | chickenchunks.gui.showlasers=Zeige Laser 6 | chickenchunks.gui.hidelasers=Verstecke Laser 7 | chickenchunks.gui.chunk=%d Chunk 8 | chickenchunks.gui.chunks=%d Chunks 9 | chickenchunks.gui.radius=Radius 10 | 11 | chickenchunks.shape.square=Quadrat 12 | chickenchunks.shape.circle=Kreis 13 | chickenchunks.shape.linex=Gerade X 14 | chickenchunks.shape.linez=Gerade Z 15 | 16 | chickenchunks.accessdenied=Dieser Chunkloader gehört dir nicht. 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Dir ist die Vervendung des ChunkViewer nicht gestattet. 20 | command.chunkloaders.alreadyopen=ChunkViewer ist bereits geöffnet. 21 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/ru_RU.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Загрузчик чанков 2 | tile.chickenChunkLoader|1.name=Загрузчик чанка 3 | 4 | chickenchunks.gui.name=Загрузчик чанков 5 | chickenchunks.gui.showlasers=Показать лазеры 6 | chickenchunks.gui.hidelasers=Скрыть Лазеры 7 | chickenchunks.gui.chunk=%d чанк 8 | chickenchunks.gui.chunks=%d чанки 9 | chickenchunks.gui.radius=Радиус 10 | 11 | chickenchunks.shape.square=Квадрат 12 | chickenchunks.shape.circle=Круг 13 | chickenchunks.shape.linex=Line X 14 | chickenchunks.shape.linez=Line Z 15 | 16 | chickenchunks.accessdenied=Этот загрузчик чанков не принадлежат вам. 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Вам не разрешено пользоваться просмотром чанков. 20 | command.chunkloaders.alreadyopen=Просмотр чанков уже открыт 21 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/tr_TR.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Chunk Yükleyici 2 | tile.chickenChunkLoader|1.name=Nokta Yükleyici 3 | 4 | chickenchunks.gui.name=Chunk Yükleyici 5 | chickenchunks.gui.showlasers=Lazerleri Aç 6 | chickenchunks.gui.hidelasers=Lazerleri Kapat 7 | chickenchunks.gui.chunk=%d Chunk 8 | chickenchunks.gui.chunks=%d Chunklar 9 | chickenchunks.gui.radius=Etki Alanı 10 | 11 | chickenchunks.shape.square=Kare 12 | chickenchunks.shape.circle=Daire 13 | chickenchunks.shape.linex=X Hattı 14 | chickenchunks.shape.linez=Z Hattı 15 | 16 | chickenchunks.accessdenied=Bu Chunk Yükleyici size ait değildir. 17 | command.ccdebug=/ccdebug [boyut] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Bu Chunk Yükleyici size ait olmadığı için kullanamazsınız. 20 | command.chunkloaders.alreadyopen=Chunk Görüntüleyicisi zaten açık. 21 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/pl_PL.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Blok ładujący chunki 2 | tile.chickenChunkLoader|1.name=Blok ładujący miejsca 3 | 4 | chickenchunks.gui.name=Blok ładujący chunki 5 | chickenchunks.gui.showlasers=Pokazuj lasery 6 | chickenchunks.gui.hidelasers=Chowaj lasery 7 | chickenchunks.gui.chunk=%d chunk 8 | chickenchunks.gui.chunks=%d chunków 9 | chickenchunks.gui.radius=Promień 10 | 11 | chickenchunks.shape.square=Kwadrat 12 | chickenchunks.shape.circle=Koło 13 | chickenchunks.shape.linex=Linia X 14 | chickenchunks.shape.linez=Linia Z 15 | 16 | chickenchunks.accessdenied=Ten Blok ładujący chunki nie należy do ciebie. 17 | command.ccdebug=/ccdebug [wymiar] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Nie maż zezwolenia na używanie Pokazywacza chunków. 20 | command.chunkloaders.alreadyopen=Pokazywacz chunków już otwarty. -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/fr_CA.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Chargeur de chunks 2 | tile.chickenChunkLoader|1.name=Chargeur de lieu 3 | 4 | chickenchunks.gui.name=Chunks à charger 5 | chickenchunks.gui.showlasers=Lasers : Non 6 | chickenchunks.gui.hidelasers=Lasers : Oui 7 | chickenchunks.gui.chunk=%d chunk 8 | chickenchunks.gui.chunks=%d chunks 9 | chickenchunks.gui.radius=Rayon 10 | 11 | chickenchunks.shape.square=Carré 12 | chickenchunks.shape.circle=Cercle 13 | chickenchunks.shape.linex=Ligne X 14 | chickenchunks.shape.linez=Ligne Z 15 | 16 | chickenchunks.accessdenied=Ce chargeur de chunks ne vous appartient pas. 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Vous n'êtes pas autorisé(e) d'utiliser le visualiseur de chunks. 20 | command.chunkloaders.alreadyopen=Le visualiseur de chunks est déjà ouvert. 21 | -------------------------------------------------------------------------------- /resources/assets/chickenchunks/lang/fr_FR.lang: -------------------------------------------------------------------------------- 1 | tile.chickenChunkLoader|0.name=Chargeur de Chunk 2 | tile.chickenChunkLoader|1.name=Chargeur de Lieu 3 | 4 | chickenchunks.gui.name=Chargeur de Chunk 5 | chickenchunks.gui.showlasers=Montrer les rayons 6 | chickenchunks.gui.hidelasers=Cacher les rayons 7 | chickenchunks.gui.chunk=%d Chunk 8 | chickenchunks.gui.chunks=%d Chunks 9 | chickenchunks.gui.radius=Rayon 10 | 11 | chickenchunks.shape.square=Carré 12 | chickenchunks.shape.circle=Cercle 13 | chickenchunks.shape.linex=Ligne X 14 | chickenchunks.shape.linez=Ligne Z 15 | 16 | chickenchunks.accessdenied=Ce Chargeur de Chunk ne vous appartient pas. 17 | command.ccdebug=/ccdebug [dimension] 18 | command.chunkloaders=/chunkloaders 19 | command.chunkloaders.denied=Vous n'êtes pas autorisé à utiliser le visualiseur de Chunks. 20 | command.chunkloaders.alreadyopen=Le visualiseur de Chunks est déjà ouvert. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ChickenChunks 2 | ============== 3 | THIS PROJECT IS RETIRED: See [here] for the current repo. 4 | 5 | As you all should know, the minecraft world is separated into chunks. 16x16 areas that are loaded depending on how close a player is to them. If you have machines or plants or anything that does something in a chunk that is not loaded because there are no players nearby nothing will happen. 6 | 7 | This mod adds a block called a chunkloader, when placed it will keep chunks around it loaded even if no players are nearby or even online. So now your plants can grow and your automatic quarries can run, even when you're not around. 8 | 9 | Current recommended and latest builds can be found at http://chickenbones.net/Pages/links.html 10 | 11 | Current maven: http://chickenbones.net/maven/ 12 | 13 | Join us on IRC: 14 | - Server: Esper.net 15 | - Channel: #ChickenBones 16 | 17 | 18 | [here]: 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 ChickenBones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderClientProxy.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.core.ClientUtils; 4 | import net.minecraft.client.Minecraft; 5 | import codechicken.core.CCUpdateChecker; 6 | import codechicken.lib.packet.PacketCustom; 7 | import cpw.mods.fml.client.registry.ClientRegistry; 8 | import cpw.mods.fml.client.registry.RenderingRegistry; 9 | 10 | import static codechicken.chunkloader.ChickenChunks.*; 11 | 12 | public class ChunkLoaderClientProxy extends ChunkLoaderProxy 13 | { 14 | @Override 15 | public void init() 16 | { 17 | if(config.getTag("checkUpdates").getBooleanValue(true)) 18 | CCUpdateChecker.updateCheck("ChickenChunks"); 19 | ClientUtils.enhanceSupportersList("ChickenChunks"); 20 | 21 | super.init(); 22 | 23 | PacketCustom.assignHandler(ChunkLoaderCPH.channel, new ChunkLoaderCPH()); 24 | 25 | ClientRegistry.bindTileEntitySpecialRenderer(TileChunkLoader.class, new TileChunkLoaderRenderer()); 26 | ClientRegistry.bindTileEntitySpecialRenderer(TileSpotLoader.class, new TileChunkLoaderRenderer()); 27 | RenderingRegistry.registerBlockHandler(new ChunkLoaderSBRH()); 28 | } 29 | 30 | @Override 31 | public void openGui(TileChunkLoader tile) 32 | { 33 | Minecraft.getMinecraft().displayGuiScreen(new GuiChunkLoader(tile)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderSPH.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import net.minecraft.entity.player.EntityPlayerMP; 4 | import net.minecraft.network.play.INetHandlerPlayServer; 5 | import net.minecraft.tileentity.TileEntity; 6 | import net.minecraft.world.World; 7 | import codechicken.lib.packet.PacketCustom; 8 | import codechicken.lib.packet.PacketCustom.IServerPacketHandler; 9 | 10 | public class ChunkLoaderSPH implements IServerPacketHandler 11 | { 12 | public static String channel = "ChickenChunks"; 13 | 14 | @Override 15 | public void handlePacket(PacketCustom packet, EntityPlayerMP sender, INetHandlerPlayServer handler) { 16 | switch(packet.getType()) 17 | { 18 | case 1: 19 | PlayerChunkViewerManager.instance().closeViewer(sender.getCommandSenderName()); 20 | break; 21 | case 2: 22 | handleChunkLoaderChangePacket(sender.worldObj, packet); 23 | break; 24 | 25 | } 26 | } 27 | 28 | private void handleChunkLoaderChangePacket(World world, PacketCustom packet) 29 | { 30 | TileEntity tile = world.getTileEntity(packet.readInt(), packet.readInt(), packet.readInt()); 31 | if(tile instanceof TileChunkLoader) 32 | { 33 | TileChunkLoader ctile = (TileChunkLoader)tile; 34 | ctile.setShapeAndRadius(ChunkLoaderShape.values()[packet.readUByte()], packet.readUByte()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderSBRH.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.world.IBlockAccess; 5 | import net.minecraft.client.renderer.RenderBlocks; 6 | import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; 7 | import cpw.mods.fml.client.registry.RenderingRegistry; 8 | 9 | public class ChunkLoaderSBRH implements ISimpleBlockRenderingHandler 10 | { 11 | public static int renderID = RenderingRegistry.getNextAvailableRenderId(); 12 | 13 | @Override 14 | public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer) 15 | { 16 | if(block != ChickenChunks.blockChunkLoader) 17 | return; 18 | 19 | ChickenChunks.blockChunkLoader.setBlockBoundsForItemRender(metadata); 20 | int actualRenderID = renderID; 21 | renderID = 0; 22 | renderer.renderBlockAsItem(block, metadata, 1); 23 | renderID = actualRenderID; 24 | } 25 | 26 | @Override 27 | public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) 28 | { 29 | if(block != ChickenChunks.blockChunkLoader) 30 | return false; 31 | 32 | ChickenChunks.blockChunkLoader.setBlockBoundsBasedOnState(world, x, y, z); 33 | renderer.renderStandardBlock(block, x, y, z); 34 | return true; 35 | } 36 | 37 | @Override 38 | public boolean shouldRender3DInInventory(int modelID) 39 | { 40 | return true; 41 | } 42 | 43 | @Override 44 | public int getRenderId() 45 | { 46 | return renderID; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/CommandChunkLoaders.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.core.commands.PlayerCommand; 4 | import net.minecraft.entity.player.EntityPlayerMP; 5 | import net.minecraft.command.ICommandSender; 6 | import net.minecraft.world.WorldServer; 7 | 8 | public class CommandChunkLoaders extends PlayerCommand 9 | { 10 | @Override 11 | public String getCommandName() 12 | { 13 | return "chunkloaders"; 14 | } 15 | 16 | @Override 17 | public String getCommandUsage(ICommandSender var1) 18 | { 19 | return "chunkloaders"; 20 | } 21 | 22 | @Override 23 | public void handleCommand(WorldServer world, EntityPlayerMP player, String[] args) 24 | { 25 | WCommandSender wrapped = new WCommandSender(player); 26 | if(PlayerChunkViewerManager.instance().isViewerOpen(player.getCommandSenderName())) 27 | { 28 | wrapped.chatT("command.chunkloaders.alreadyopen"); 29 | return; 30 | } 31 | if(!ChunkLoaderManager.allowChunkViewer(player.getCommandSenderName())) 32 | { 33 | wrapped.chatT("command.chunkloaders.denied"); 34 | return; 35 | } 36 | PlayerChunkViewerManager.instance().addViewers.add(player.getCommandSenderName()); 37 | } 38 | 39 | @Override 40 | public void printHelp(WCommandSender listener) 41 | { 42 | listener.chatT("command.chunkloaders"); 43 | } 44 | 45 | @Override 46 | public boolean OPOnly() 47 | { 48 | return false; 49 | } 50 | 51 | @Override 52 | public int minimumParameters() 53 | { 54 | return 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/TileSpotLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | import java.util.HashSet; 6 | 7 | import net.minecraft.network.Packet; 8 | import net.minecraft.world.ChunkCoordIntPair; 9 | import net.minecraft.tileentity.TileEntity; 10 | import net.minecraft.world.World; 11 | import codechicken.lib.packet.PacketCustom; 12 | 13 | public class TileSpotLoader extends TileChunkLoaderBase 14 | { 15 | public static void handleDescriptionPacket(PacketCustom packet, World world) { 16 | TileEntity tile = world.getTileEntity(packet.readInt(), packet.readInt(), packet.readInt()); 17 | if (tile instanceof TileSpotLoader) { 18 | TileSpotLoader ctile = (TileSpotLoader) tile; 19 | ctile.active = packet.readBoolean(); 20 | if (packet.readBoolean()) 21 | ctile.owner = packet.readString(); 22 | } 23 | } 24 | 25 | public Packet getDescriptionPacket() { 26 | PacketCustom packet = new PacketCustom(ChunkLoaderSPH.channel, 11); 27 | packet.writeCoord(xCoord, yCoord, zCoord); 28 | packet.writeBoolean(active); 29 | packet.writeBoolean(owner != null); 30 | if (owner != null) 31 | packet.writeString(owner); 32 | return packet.toPacket(); 33 | } 34 | 35 | @Override 36 | public Collection getChunks() { 37 | return Arrays.asList(getChunkPosition()); 38 | } 39 | 40 | public static HashSet getContainedChunks(ChunkLoaderShape shape, int xCoord, int zCoord, int radius) { 41 | return shape.getLoadedChunks(xCoord >> 4, zCoord >> 4, radius - 1); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChickenChunks.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.io.File; 4 | 5 | import codechicken.core.launch.CodeChickenCorePlugin; 6 | import codechicken.lib.config.ConfigFile; 7 | import cpw.mods.fml.common.Mod; 8 | import cpw.mods.fml.common.Mod.EventHandler; 9 | import cpw.mods.fml.common.SidedProxy; 10 | import cpw.mods.fml.common.Mod.Instance; 11 | import cpw.mods.fml.common.event.FMLInitializationEvent; 12 | import cpw.mods.fml.common.event.FMLPreInitializationEvent; 13 | import cpw.mods.fml.common.event.FMLServerStartingEvent; 14 | 15 | @Mod(modid = "ChickenChunks", dependencies = "required-after:CodeChickenCore@[" + CodeChickenCorePlugin.version + ",)", acceptedMinecraftVersions = CodeChickenCorePlugin.mcVersion) 16 | public class ChickenChunks 17 | { 18 | @SidedProxy(clientSide = "codechicken.chunkloader.ChunkLoaderClientProxy", serverSide = "codechicken.chunkloader.ChunkLoaderProxy") 19 | public static ChunkLoaderProxy proxy; 20 | 21 | public static ConfigFile config; 22 | 23 | public static BlockChunkLoader blockChunkLoader; 24 | 25 | @Instance(value = "ChickenChunks") 26 | public static ChickenChunks instance; 27 | 28 | @EventHandler 29 | public void preInit(FMLPreInitializationEvent event) { 30 | config = new ConfigFile(new File(event.getModConfigurationDirectory(), "ChickenChunks.cfg")) 31 | .setComment("ChunkLoader Configuration File\nDeleting any element will restore it to it's default value\nBlock ID's will be automatically generated the first time it's run"); 32 | } 33 | 34 | @EventHandler 35 | public void init(FMLInitializationEvent event) { 36 | proxy.init(); 37 | } 38 | 39 | @EventHandler 40 | public void serverStarting(FMLServerStartingEvent event) { 41 | proxy.registerCommands(event); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderProxy.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import cpw.mods.fml.common.FMLCommonHandler; 4 | import net.minecraft.command.CommandHandler; 5 | import net.minecraft.creativetab.CreativeTabs; 6 | import net.minecraft.init.Blocks; 7 | import net.minecraft.init.Items; 8 | import net.minecraft.item.ItemStack; 9 | import net.minecraftforge.common.MinecraftForge; 10 | import codechicken.lib.packet.PacketCustom; 11 | import cpw.mods.fml.common.event.FMLServerStartingEvent; 12 | import cpw.mods.fml.common.registry.GameRegistry; 13 | 14 | import static codechicken.chunkloader.ChickenChunks.*; 15 | 16 | public class ChunkLoaderProxy 17 | { 18 | public void init() 19 | { 20 | blockChunkLoader = new BlockChunkLoader(); 21 | blockChunkLoader.setBlockName("chickenChunkLoader").setCreativeTab(CreativeTabs.tabMisc); 22 | GameRegistry.registerBlock(blockChunkLoader, ItemChunkLoader.class, "chickenChunkLoader"); 23 | 24 | GameRegistry.registerTileEntity(TileChunkLoader.class, "ChickenChunkLoader"); 25 | GameRegistry.registerTileEntity(TileSpotLoader.class, "ChickenSpotLoader"); 26 | 27 | PacketCustom.assignHandler(ChunkLoaderSPH.channel, new ChunkLoaderSPH()); 28 | ChunkLoaderManager.initConfig(config); 29 | 30 | MinecraftForge.EVENT_BUS.register(new ChunkLoaderEventHandler()); 31 | FMLCommonHandler.instance().bus().register(new ChunkLoaderEventHandler()); 32 | ChunkLoaderManager.registerMod(instance); 33 | 34 | GameRegistry.addRecipe(new ItemStack(blockChunkLoader, 1, 0), 35 | " p ", 36 | "ggg", 37 | "gEg", 38 | 'p', Items.ender_pearl, 39 | 'g', Items.gold_ingot, 40 | 'd', Items.diamond, 41 | 'E', Blocks.enchanting_table 42 | ); 43 | 44 | GameRegistry.addRecipe(new ItemStack(blockChunkLoader, 10, 1), 45 | "ppp", 46 | "pcp", 47 | "ppp", 48 | 'p', Items.ender_pearl, 49 | 'c', new ItemStack(blockChunkLoader, 1, 0) 50 | ); 51 | } 52 | 53 | public void registerCommands(FMLServerStartingEvent event) 54 | { 55 | CommandHandler commandManager = (CommandHandler)event.getServer().getCommandManager(); 56 | commandManager.registerCommand(new CommandChunkLoaders()); 57 | commandManager.registerCommand(new CommandDebugInfo()); 58 | } 59 | 60 | public void openGui(TileChunkLoader tile) 61 | { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderCPH.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.network.play.INetHandlerPlayClient; 5 | import net.minecraft.tileentity.TileEntity; 6 | import net.minecraft.world.ChunkCoordIntPair; 7 | import codechicken.core.CommonUtils; 8 | import codechicken.lib.packet.PacketCustom; 9 | import codechicken.lib.packet.PacketCustom.IClientPacketHandler; 10 | import codechicken.lib.vec.BlockCoord; 11 | import codechicken.lib.vec.Vector3; 12 | 13 | public class ChunkLoaderCPH implements IClientPacketHandler 14 | { 15 | public static String channel = "ChickenChunks"; 16 | 17 | @Override 18 | public void handlePacket(PacketCustom packet, Minecraft mc, INetHandlerPlayClient handler) { 19 | switch (packet.getType()) { 20 | case 1: 21 | PlayerChunkViewer.openViewer((int) mc.thePlayer.posX, (int) mc.thePlayer.posZ, CommonUtils.getDimension(mc.theWorld)); 22 | break; 23 | case 2: 24 | PlayerChunkViewer.instance().loadDimension(packet, mc.theWorld); 25 | break; 26 | case 3: 27 | PlayerChunkViewer.instance().unloadDimension(packet.readInt()); 28 | break; 29 | case 4: 30 | PlayerChunkViewer.instance().handleChunkChange( 31 | packet.readInt(), 32 | new ChunkCoordIntPair(packet.readInt(), packet.readInt()), 33 | packet.readBoolean()); 34 | break; 35 | case 5: 36 | PlayerChunkViewer.instance().handleTicketChange( 37 | packet.readInt(), 38 | packet.readInt(), 39 | new ChunkCoordIntPair(packet.readInt(), packet.readInt()), 40 | packet.readBoolean()); 41 | break; 42 | case 6: 43 | PlayerChunkViewer.instance().handlePlayerUpdate( 44 | packet.readString(), packet.readInt(), 45 | new Vector3(packet.readFloat(), packet.readFloat(), packet.readFloat())); 46 | break; 47 | case 7: 48 | PlayerChunkViewer.instance().removePlayer(packet.readString()); 49 | break; 50 | case 8: 51 | PlayerChunkViewer.instance().handleNewTicket(packet, mc.theWorld); 52 | break; 53 | case 10: 54 | TileChunkLoader.handleDescriptionPacket(packet, mc.theWorld); 55 | break; 56 | case 11: 57 | TileSpotLoader.handleDescriptionPacket(packet, mc.theWorld); 58 | break; 59 | case 12: 60 | BlockCoord pos = packet.readCoord(); 61 | TileEntity tile = mc.theWorld.getTileEntity(pos.x, pos.y, pos.z); 62 | if (tile instanceof TileChunkLoader) 63 | mc.displayGuiScreen(new GuiChunkLoader((TileChunkLoader) tile)); 64 | break; 65 | 66 | } 67 | } 68 | 69 | public static void sendGuiClosing() { 70 | PacketCustom packet = new PacketCustom(channel, 1); 71 | packet.sendToServer(); 72 | } 73 | 74 | public static void sendShapeChange(TileChunkLoader tile, ChunkLoaderShape shape, int radius) { 75 | PacketCustom packet = new PacketCustom(channel, 2); 76 | packet.writeCoord(tile.xCoord, tile.yCoord, tile.zCoord); 77 | packet.writeByte(shape.ordinal()); 78 | packet.writeByte(radius); 79 | packet.sendToServer(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderEventHandler.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.chunkloader.PlayerChunkViewerManager.DimensionChange; 4 | import codechicken.chunkloader.PlayerChunkViewerManager.TicketChange; 5 | import codechicken.core.ServerUtils; 6 | 7 | import cpw.mods.fml.common.eventhandler.SubscribeEvent; 8 | import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; 9 | import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent; 10 | import cpw.mods.fml.common.gameevent.TickEvent.Phase; 11 | import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent; 12 | import cpw.mods.fml.common.gameevent.TickEvent.WorldTickEvent; 13 | import net.minecraft.world.WorldServer; 14 | import net.minecraftforge.common.ForgeChunkManager.ForceChunkEvent; 15 | import net.minecraftforge.common.ForgeChunkManager.UnforceChunkEvent; 16 | import net.minecraftforge.event.world.ChunkDataEvent; 17 | import net.minecraftforge.event.world.WorldEvent.Load; 18 | import net.minecraftforge.event.world.WorldEvent.Save; 19 | import net.minecraftforge.event.world.WorldEvent.Unload; 20 | 21 | public class ChunkLoaderEventHandler 22 | { 23 | @SubscribeEvent 24 | public void serverTick(ServerTickEvent event) { 25 | if (event.phase == Phase.END) 26 | PlayerChunkViewerManager.instance().update(); 27 | } 28 | 29 | @SubscribeEvent 30 | public void worldTick(WorldTickEvent event) { 31 | if (event.phase == Phase.END && !event.world.isRemote) { 32 | ChunkLoaderManager.tickEnd((WorldServer) event.world); 33 | PlayerChunkViewerManager.instance().calculateChunkChanges((WorldServer) event.world); 34 | } 35 | } 36 | 37 | @SubscribeEvent 38 | public void playerLogin(PlayerLoggedInEvent event) { 39 | ChunkLoaderManager.playerLogin(event.player.getCommandSenderName()); 40 | } 41 | 42 | @SubscribeEvent 43 | public void playerLogout(PlayerLoggedOutEvent event) { 44 | PlayerChunkViewerManager.instance().logouts.add(event.player.getCommandSenderName()); 45 | ChunkLoaderManager.playerLogout(event.player.getCommandSenderName()); 46 | } 47 | 48 | @SubscribeEvent 49 | public void onChunkDataLoad(ChunkDataEvent.Load event) { 50 | ChunkLoaderManager.load((WorldServer) event.world); 51 | } 52 | 53 | @SubscribeEvent 54 | public void onWorldLoad(Load event) { 55 | if (!event.world.isRemote) { 56 | ChunkLoaderManager.load((WorldServer) event.world); 57 | ChunkLoaderManager.loadWorld((WorldServer) event.world); 58 | PlayerChunkViewerManager.instance().dimChanges.add(new DimensionChange((WorldServer) event.world, true)); 59 | } 60 | } 61 | 62 | @SubscribeEvent 63 | public void onWorldUnload(Unload event) { 64 | if (!event.world.isRemote) { 65 | if (ServerUtils.mc().isServerRunning()) { 66 | ChunkLoaderManager.unloadWorld(event.world); 67 | PlayerChunkViewerManager.instance().dimChanges.add(new DimensionChange((WorldServer) event.world, false)); 68 | } else { 69 | PlayerChunkViewerManager.serverShutdown(); 70 | ChunkLoaderManager.serverShutdown(); 71 | } 72 | } 73 | } 74 | 75 | @SubscribeEvent 76 | public void onWorldSave(Save event) { 77 | ChunkLoaderManager.save((WorldServer) event.world); 78 | } 79 | 80 | @SubscribeEvent 81 | public void onChunkForce(ForceChunkEvent event) { 82 | PlayerChunkViewerManager.instance().ticketChanges.add(new TicketChange(event.ticket, event.location, true)); 83 | } 84 | 85 | @SubscribeEvent 86 | public void onChunkUnForce(UnforceChunkEvent event) { 87 | PlayerChunkViewerManager.instance().ticketChanges.add(new TicketChange(event.ticket, event.location, false)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/TileChunkLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | 6 | import net.minecraft.network.Packet; 7 | import net.minecraft.world.ChunkCoordIntPair; 8 | import net.minecraft.nbt.NBTTagCompound; 9 | import net.minecraft.tileentity.TileEntity; 10 | import net.minecraft.world.World; 11 | import codechicken.lib.packet.PacketCustom; 12 | 13 | public class TileChunkLoader extends TileChunkLoaderBase 14 | { 15 | public static void handleDescriptionPacket(PacketCustom packet, World world) 16 | { 17 | TileEntity tile = world.getTileEntity(packet.readInt(), packet.readInt(), packet.readInt()); 18 | if(tile instanceof TileChunkLoader) 19 | { 20 | TileChunkLoader ctile = (TileChunkLoader)tile; 21 | ctile.setShapeAndRadius(ChunkLoaderShape.values()[packet.readUByte()], packet.readUByte()); 22 | ctile.active = packet.readBoolean(); 23 | if(packet.readBoolean()) 24 | ctile.owner = packet.readString(); 25 | } 26 | } 27 | 28 | public boolean setShapeAndRadius(ChunkLoaderShape newShape, int newRadius) 29 | { 30 | if(worldObj.isRemote) 31 | { 32 | radius = newRadius; 33 | shape = newShape; 34 | return true; 35 | } 36 | Collection chunks = getContainedChunks(newShape, xCoord, zCoord, newRadius); 37 | if(chunks.size() > ChunkLoaderManager.maxChunksPerLoader()) 38 | { 39 | return false; 40 | } 41 | else if(powered) 42 | { 43 | radius = newRadius; 44 | shape = newShape; 45 | worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); 46 | return true; 47 | } 48 | else if(ChunkLoaderManager.canLoaderAdd(this, chunks)) 49 | { 50 | radius = newRadius; 51 | shape = newShape; 52 | ChunkLoaderManager.updateLoader(this); 53 | worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | public Packet getDescriptionPacket() 60 | { 61 | PacketCustom packet = new PacketCustom(ChunkLoaderSPH.channel, 10); 62 | packet.writeCoord(xCoord, yCoord, zCoord); 63 | packet.writeByte(shape.ordinal()); 64 | packet.writeByte(radius); 65 | packet.writeBoolean(active); 66 | packet.writeBoolean(owner != null); 67 | if(owner != null) 68 | packet.writeString(owner); 69 | return packet.toPacket(); 70 | } 71 | 72 | public void writeToNBT(NBTTagCompound tag) 73 | { 74 | super.writeToNBT(tag); 75 | tag.setByte("radius", (byte) radius); 76 | tag.setByte("shape", (byte) shape.ordinal()); 77 | } 78 | 79 | @Override 80 | public void readFromNBT(NBTTagCompound tag) 81 | { 82 | super.readFromNBT(tag); 83 | radius = tag.getByte("radius"); 84 | shape = ChunkLoaderShape.values()[tag.getByte("shape")]; 85 | } 86 | 87 | @Override 88 | public HashSet getChunks() 89 | { 90 | return getContainedChunks(shape, xCoord, zCoord, radius); 91 | } 92 | 93 | public static HashSet getContainedChunks(ChunkLoaderShape shape, int xCoord, int zCoord, int radius) 94 | { 95 | return shape.getLoadedChunks(xCoord >> 4, zCoord >> 4, radius - 1); 96 | } 97 | 98 | public int countLoadedChunks() 99 | { 100 | return getChunks().size(); 101 | } 102 | 103 | @Override 104 | public void activate() 105 | { 106 | if(radius == 0) 107 | { 108 | //create a small one and try and increment it to 2 109 | radius = 1; 110 | shape = ChunkLoaderShape.Square; 111 | setShapeAndRadius(ChunkLoaderShape.Square, 2); 112 | } 113 | 114 | super.activate(); 115 | } 116 | 117 | public int radius; 118 | public ChunkLoaderShape shape = ChunkLoaderShape.Square; 119 | } 120 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderShape.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.HashSet; 4 | 5 | import net.minecraft.util.StatCollector; 6 | import net.minecraft.world.ChunkCoordIntPair; 7 | 8 | public enum ChunkLoaderShape 9 | { 10 | Square("square"), 11 | Circle("circle"), 12 | LineX("linex"), 13 | LineZ("linez"); 14 | 15 | String name; 16 | 17 | private ChunkLoaderShape(String s) { 18 | name = s; 19 | } 20 | 21 | public HashSet getChunks(int radius, ChunkCoordIntPair center) { 22 | HashSet chunkset = new HashSet(); 23 | radius -= 1; 24 | switch (this) { 25 | case Square: 26 | for (int x = center.chunkXPos - radius; x <= center.chunkXPos + radius; x++) { 27 | for (int z = center.chunkZPos - radius; z <= center.chunkZPos + radius; z++) { 28 | chunkset.add(new ChunkCoordIntPair(x, z)); 29 | } 30 | } 31 | break; 32 | case LineX: 33 | for (int x = center.chunkXPos - radius; x <= center.chunkXPos + radius; x++) { 34 | chunkset.add(new ChunkCoordIntPair(x, center.chunkZPos)); 35 | } 36 | break; 37 | case LineZ: 38 | for (int z = center.chunkZPos - radius; z <= center.chunkZPos + radius; z++) { 39 | chunkset.add(new ChunkCoordIntPair(center.chunkXPos, z)); 40 | } 41 | break; 42 | case Circle: 43 | for (int x = center.chunkXPos - radius; x <= center.chunkXPos + radius; x++) { 44 | for (int z = center.chunkZPos - radius; z <= center.chunkZPos + radius; z++) { 45 | int relx = x - center.chunkXPos; 46 | int relz = z - center.chunkZPos; 47 | double dist = Math.sqrt(relx * relx + relz * relz); 48 | if (dist <= radius) 49 | chunkset.add(new ChunkCoordIntPair(x, z)); 50 | } 51 | } 52 | } 53 | return chunkset; 54 | } 55 | 56 | public ChunkLoaderShape next() { 57 | int index = ordinal(); 58 | index++; 59 | if (index == values().length) 60 | index = 0; 61 | return values()[index]; 62 | } 63 | 64 | public ChunkLoaderShape prev() { 65 | int index = ordinal(); 66 | index--; 67 | if (index == -1) 68 | index = values().length - 1; 69 | return values()[index]; 70 | } 71 | 72 | public HashSet getLoadedChunks(int chunkx, int chunkz, int radius) { 73 | HashSet chunkSet = new HashSet(); 74 | switch (this) { 75 | case Square: 76 | for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) { 77 | for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) { 78 | chunkSet.add(new ChunkCoordIntPair(cx, cz)); 79 | } 80 | } 81 | break; 82 | case LineX: 83 | for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) { 84 | chunkSet.add(new ChunkCoordIntPair(cx, chunkz)); 85 | } 86 | break; 87 | case LineZ: 88 | for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) { 89 | chunkSet.add(new ChunkCoordIntPair(chunkx, cz)); 90 | } 91 | break; 92 | case Circle: 93 | for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) { 94 | for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) { 95 | double distSquared = (cx - chunkx) * (cx - chunkx) + (cz - chunkz) * (cz - chunkz); 96 | if (distSquared <= radius * radius) 97 | chunkSet.add(new ChunkCoordIntPair(cx, cz)); 98 | } 99 | } 100 | 101 | } 102 | return chunkSet; 103 | } 104 | 105 | public String getName() { 106 | return StatCollector.translateToLocal("chickenchunks.shape." + name); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/GuiChunkLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.lib.util.LangProxy; 4 | import net.minecraft.client.gui.GuiButton; 5 | import net.minecraft.client.gui.GuiScreen; 6 | 7 | import org.lwjgl.opengl.GL11; 8 | 9 | import codechicken.lib.render.CCRenderState; 10 | 11 | public class GuiChunkLoader extends GuiScreen 12 | { 13 | private static LangProxy lang = new LangProxy("chickenchunks.gui"); 14 | 15 | public GuiButton laserButton; 16 | public GuiButton shapeButton; 17 | public TileChunkLoader tile; 18 | 19 | public GuiChunkLoader(TileChunkLoader tile) { 20 | this.tile = tile; 21 | } 22 | 23 | public void initGui() { 24 | buttonList.clear(); 25 | 26 | buttonList.add(new GuiButton(1, width / 2 - 20, height / 2 - 45, 20, 20, "+")); 27 | buttonList.add(new GuiButton(2, width / 2 - 80, height / 2 - 45, 20, 20, "-")); 28 | buttonList.add(laserButton = new GuiButton(3, width / 2 + 7, height / 2 - 60, 75, 20, "-")); 29 | buttonList.add(shapeButton = new GuiButton(4, width / 2 + 7, height / 2 - 37, 75, 20, "-")); 30 | updateNames(); 31 | 32 | super.initGui(); 33 | } 34 | 35 | public void updateNames() { 36 | laserButton.displayString = tile.renderInfo.showLasers ? lang.translate("hidelasers") : lang.translate("showlasers"); 37 | shapeButton.displayString = tile.shape.getName(); 38 | } 39 | 40 | public void updateScreen() { 41 | if (mc.theWorld.getTileEntity(tile.xCoord, tile.yCoord, tile.zCoord) != tile)//tile changed 42 | { 43 | mc.currentScreen = null; 44 | mc.setIngameFocus(); 45 | } 46 | updateNames(); 47 | super.updateScreen(); 48 | } 49 | 50 | public void drawScreen(int i, int j, float f) { 51 | drawDefaultBackground(); 52 | drawContainerBackground(); 53 | 54 | super.drawScreen(i, j, f);//buttons 55 | 56 | GL11.glDisable(GL11.GL_LIGHTING); 57 | GL11.glDisable(GL11.GL_DEPTH_TEST); 58 | 59 | drawCentered(lang.translate("name"), width / 2 - 40, height / 2 - 74, 0x303030); 60 | if (tile.owner != null) 61 | drawCentered(tile.owner, width / 2 + 44, height / 2 - 72, 0x801080); 62 | drawCentered(lang.translate("radius"), width / 2 - 40, height / 2 - 57, 0x404040); 63 | drawCentered("" + tile.radius, width / 2 - 40, height / 2 - 39, 0xFFFFFF); 64 | 65 | int chunks = tile.countLoadedChunks(); 66 | drawCentered(lang.format(chunks == 1 ? "chunk" : "chunks", chunks), width / 2 - 39, height / 2 - 21, 0x108000); 67 | 68 | //TODO: sradius = "Total "+ChunkLoaderManager.activeChunkLoaders+"/"+ChunkLoaderManager.allowedChunkloaders+" Chunks"; 69 | //fontRenderer.drawString(sradius, width / 2 - fontRenderer.getStringWidth(sradius) / 2, height / 2 - 8, 0x108000); 70 | 71 | GL11.glEnable(GL11.GL_LIGHTING); 72 | GL11.glEnable(GL11.GL_DEPTH_TEST); 73 | } 74 | 75 | private void drawCentered(String s, int x, int y, int colour) { 76 | fontRendererObj.drawString(s, x - fontRendererObj.getStringWidth(s) / 2, y, colour); 77 | } 78 | 79 | int button; 80 | 81 | @Override 82 | protected void mouseClicked(int par1, int par2, int par3) { 83 | button = par3; 84 | if (par3 == 1) 85 | par3 = 0; 86 | super.mouseClicked(par1, par2, par3); 87 | } 88 | 89 | protected void actionPerformed(GuiButton guibutton) { 90 | if (guibutton.id == 1) 91 | ChunkLoaderCPH.sendShapeChange(tile, tile.shape, tile.radius + 1); 92 | if (guibutton.id == 2 && tile.radius > 1) 93 | ChunkLoaderCPH.sendShapeChange(tile, tile.shape, tile.radius - 1); 94 | if (guibutton.id == 3) 95 | tile.renderInfo.showLasers = !tile.renderInfo.showLasers; 96 | if (guibutton.id == 4) 97 | ChunkLoaderCPH.sendShapeChange(tile, button == 1 ? tile.shape.prev() : tile.shape.next(), tile.radius); 98 | } 99 | 100 | private void drawContainerBackground() { 101 | GL11.glColor4f(1, 1, 1, 1); 102 | CCRenderState.changeTexture("chickenchunks:textures/gui/guiSmall.png"); 103 | int posx = width / 2 - 88; 104 | int posy = height / 2 - 83; 105 | drawTexturedModalRect(posx, posy, 0, 0, 176, 166); 106 | } 107 | 108 | public boolean doesGuiPauseGame() { 109 | return false; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/TileChunkLoaderBase.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import codechicken.chunkloader.TileChunkLoaderRenderer.RenderInfo; 4 | import codechicken.lib.vec.BlockCoord; 5 | import net.minecraft.world.ChunkCoordIntPair; 6 | import net.minecraft.entity.EntityLivingBase; 7 | import net.minecraft.entity.player.EntityPlayer; 8 | import net.minecraft.nbt.NBTTagCompound; 9 | import net.minecraft.tileentity.TileEntity; 10 | import net.minecraft.util.AxisAlignedBB; 11 | import net.minecraft.world.World; 12 | 13 | public abstract class TileChunkLoaderBase extends TileEntity implements IChickenChunkLoader 14 | { 15 | public void writeToNBT(NBTTagCompound tag) { 16 | super.writeToNBT(tag); 17 | tag.setBoolean("powered", powered); 18 | if (owner != null) 19 | tag.setString("owner", owner); 20 | } 21 | 22 | @Override 23 | public void readFromNBT(NBTTagCompound tag) { 24 | super.readFromNBT(tag); 25 | if (tag.hasKey("owner")) 26 | owner = tag.getString("owner"); 27 | if (tag.hasKey("powered")) 28 | powered = tag.getBoolean("powered"); 29 | loaded = true; 30 | } 31 | 32 | public void validate() { 33 | super.validate(); 34 | if (!worldObj.isRemote && loaded && !powered) 35 | activate(); 36 | 37 | if (worldObj.isRemote) 38 | renderInfo = new RenderInfo(); 39 | } 40 | 41 | public boolean isPowered() { 42 | return isPoweringTo(worldObj, xCoord, yCoord + 1, zCoord, 0) || 43 | isPoweringTo(worldObj, xCoord, yCoord - 1, zCoord, 1) || 44 | isPoweringTo(worldObj, xCoord, yCoord, zCoord + 1, 2) || 45 | isPoweringTo(worldObj, xCoord, yCoord, zCoord - 1, 3) || 46 | isPoweringTo(worldObj, xCoord + 1, yCoord, zCoord, 4) || 47 | isPoweringTo(worldObj, xCoord - 1, yCoord, zCoord, 5); 48 | } 49 | 50 | public static boolean isPoweringTo(World world, int x, int y, int z, int side) { 51 | return world.getBlock(x, y, z).isProvidingWeakPower(world, x, y, z, side) > 0; 52 | } 53 | 54 | public void invalidate() { 55 | super.invalidate(); 56 | if (!worldObj.isRemote) 57 | deactivate(); 58 | } 59 | 60 | public void destroyBlock() { 61 | ChickenChunks.blockChunkLoader.dropBlockAsItem(worldObj, xCoord, yCoord, zCoord, 0, 0); 62 | worldObj.setBlockToAir(xCoord, yCoord, zCoord); 63 | } 64 | 65 | public ChunkCoordIntPair getChunkPosition() { 66 | return new ChunkCoordIntPair(xCoord >> 4, zCoord >> 4); 67 | } 68 | 69 | public void onBlockPlacedBy(EntityLivingBase entityliving) { 70 | if (entityliving instanceof EntityPlayer) 71 | owner = entityliving.getCommandSenderName(); 72 | if (owner.equals("")) 73 | owner = null; 74 | activate(); 75 | } 76 | 77 | @Override 78 | public String getOwner() { 79 | return owner; 80 | } 81 | 82 | @Override 83 | public Object getMod() { 84 | return ChickenChunks.instance; 85 | } 86 | 87 | @Override 88 | public World getWorld() { 89 | return worldObj; 90 | } 91 | 92 | @Override 93 | public BlockCoord getPosition() { 94 | return new BlockCoord(this); 95 | } 96 | 97 | @Override 98 | public void deactivate() { 99 | loaded = true; 100 | active = false; 101 | ChunkLoaderManager.remChunkLoader(this); 102 | worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); 103 | } 104 | 105 | public void activate() { 106 | loaded = true; 107 | active = true; 108 | ChunkLoaderManager.addChunkLoader(this); 109 | worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); 110 | } 111 | 112 | @Override 113 | public void updateEntity() { 114 | if (!worldObj.isRemote) { 115 | boolean nowPowered = isPowered(); 116 | if (powered != nowPowered) { 117 | powered = nowPowered; 118 | if (powered) 119 | deactivate(); 120 | else 121 | activate(); 122 | } 123 | } else { 124 | renderInfo.update(this); 125 | } 126 | } 127 | 128 | @Override 129 | public AxisAlignedBB getRenderBoundingBox() { 130 | return INFINITE_EXTENT_AABB; 131 | } 132 | 133 | public String owner; 134 | protected boolean loaded = false; 135 | protected boolean powered = false; 136 | public RenderInfo renderInfo; 137 | public boolean active = false; 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/BlockChunkLoader.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.List; 4 | 5 | import codechicken.core.ServerUtils; 6 | import codechicken.lib.packet.PacketCustom; 7 | import cpw.mods.fml.relauncher.Side; 8 | import cpw.mods.fml.relauncher.SideOnly; 9 | 10 | import net.minecraft.block.BlockContainer; 11 | import net.minecraft.client.renderer.texture.IIconRegister; 12 | import net.minecraft.creativetab.CreativeTabs; 13 | import net.minecraft.entity.Entity; 14 | import net.minecraft.entity.EntityLivingBase; 15 | import net.minecraft.entity.player.EntityPlayer; 16 | import net.minecraft.item.Item; 17 | import net.minecraft.util.ChatComponentTranslation; 18 | import net.minecraft.world.IBlockAccess; 19 | import net.minecraft.item.ItemStack; 20 | import net.minecraft.block.material.Material; 21 | import net.minecraft.tileentity.TileEntity; 22 | import net.minecraft.util.AxisAlignedBB; 23 | import net.minecraft.util.IIcon; 24 | import net.minecraft.world.World; 25 | import net.minecraftforge.common.util.ForgeDirection; 26 | 27 | public class BlockChunkLoader extends BlockContainer 28 | { 29 | @SideOnly(Side.CLIENT) 30 | IIcon[][] icons; 31 | 32 | public BlockChunkLoader() { 33 | super(Material.rock); 34 | setHardness(20F); 35 | setResistance(100F); 36 | setStepSound(soundTypeStone); 37 | } 38 | 39 | @Override 40 | public boolean isNormalCube(IBlockAccess world, int x, int y, int z) { 41 | return false; 42 | } 43 | 44 | @Override 45 | public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) { 46 | setBlockBoundsForItemRender(world.getBlockMetadata(x, y, z)); 47 | } 48 | 49 | @Override 50 | public void addCollisionBoxesToList(World world, int x, int y, int z, AxisAlignedBB par5AxisAlignedBB, List par6List, Entity par7Entity) { 51 | setBlockBoundsBasedOnState(world, x, y, z); 52 | super.addCollisionBoxesToList(world, x, y, z, par5AxisAlignedBB, par6List, par7Entity); 53 | } 54 | 55 | @Override 56 | public boolean isSideSolid(IBlockAccess world, int x, int y, int z, ForgeDirection side) { 57 | if (world.getBlockMetadata(x, y, z) == 1) 58 | return false; 59 | 60 | return side == ForgeDirection.DOWN; 61 | } 62 | 63 | @Override 64 | public boolean canConnectRedstone(IBlockAccess world, int x, int y, int z, int side) { 65 | return true; 66 | } 67 | 68 | public void setBlockBoundsForItemRender(int metadata) { 69 | switch (metadata) { 70 | case 0: 71 | setBlockBounds(0, 0, 0, 1, 0.75F, 1); 72 | break; 73 | case 1: 74 | setBlockBounds(0.25F, 0, 0.25F, 0.75F, 0.4375F, 0.75F); 75 | break; 76 | } 77 | } 78 | 79 | @Override 80 | public IIcon getIcon(int side, int meta) { 81 | return icons[meta][side > 2 ? 2 : side]; 82 | } 83 | 84 | @Override 85 | public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int par6, float par7, float par8, float par9) { 86 | int meta = world.getBlockMetadata(x, y, z); 87 | if (meta != 0 || player.isSneaking()) 88 | return false; 89 | 90 | if (!world.isRemote) { 91 | TileChunkLoader tile = (TileChunkLoader) world.getTileEntity(x, y, z); 92 | if (tile.owner == null || tile.owner.equals(player.getCommandSenderName()) || 93 | ChunkLoaderManager.opInteract() && ServerUtils.isPlayerOP(player.getCommandSenderName())) { 94 | PacketCustom packet = new PacketCustom(ChunkLoaderSPH.channel, 12); 95 | packet.writeCoord(x, y, z); 96 | packet.sendToPlayer(player); 97 | } else 98 | player.addChatMessage(new ChatComponentTranslation("chickenchunks.accessdenied")); 99 | } 100 | return true; 101 | } 102 | 103 | @Override 104 | public void onBlockPlacedBy(World world, int i, int j, int k, EntityLivingBase entityliving, ItemStack itemstack) { 105 | if (world.isRemote) 106 | return; 107 | 108 | TileChunkLoaderBase ctile = (TileChunkLoaderBase) world.getTileEntity(i, j, k); 109 | ctile.onBlockPlacedBy(entityliving); 110 | } 111 | 112 | @Override 113 | public TileEntity createNewTileEntity(World world, int meta) { 114 | if (meta == 0) 115 | return new TileChunkLoader(); 116 | else if (meta == 1) 117 | return new TileSpotLoader(); 118 | else 119 | return null; 120 | } 121 | 122 | @Override 123 | public void registerBlockIcons(IIconRegister par1IconRegister) { 124 | icons = new IIcon[2][3]; 125 | for (int m = 0; m < icons.length; m++) 126 | for (int i = 0; i < icons[m].length; i++) 127 | icons[m][i] = par1IconRegister.registerIcon("chickenchunks:block_" + m + "_" + i); 128 | } 129 | 130 | @Override 131 | public boolean isOpaqueCube() { 132 | return false; 133 | } 134 | 135 | @Override 136 | public void getSubBlocks(Item par1, CreativeTabs par2CreativeTabs, List list) { 137 | list.add(new ItemStack(this, 1, 0)); 138 | list.add(new ItemStack(this, 1, 1)); 139 | } 140 | 141 | @Override 142 | public int damageDropped(int par1) { 143 | return par1; 144 | } 145 | 146 | @Override 147 | @SideOnly(Side.CLIENT) 148 | public int getRenderType() { 149 | return ChunkLoaderSBRH.renderID; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/PlayerChunkViewerTracker.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Map.Entry; 8 | 9 | import net.minecraft.world.chunk.Chunk; 10 | import net.minecraft.world.ChunkCoordIntPair; 11 | import net.minecraft.entity.Entity; 12 | import net.minecraft.entity.player.EntityPlayer; 13 | import net.minecraft.world.WorldServer; 14 | import net.minecraftforge.common.DimensionManager; 15 | import net.minecraftforge.common.ForgeChunkManager; 16 | import net.minecraftforge.common.ForgeChunkManager.Ticket; 17 | import codechicken.chunkloader.PlayerChunkViewerManager.ChunkChange; 18 | import codechicken.chunkloader.PlayerChunkViewerManager.TicketChange; 19 | import codechicken.core.CommonUtils; 20 | import codechicken.lib.packet.PacketCustom; 21 | import codechicken.lib.vec.Vector3; 22 | import static codechicken.chunkloader.ChunkLoaderSPH.channel; 23 | 24 | public class PlayerChunkViewerTracker 25 | { 26 | private final PlayerChunkViewerManager manager; 27 | public final EntityPlayer owner; 28 | private HashSet knownTickets = new HashSet(); 29 | public PlayerChunkViewerTracker(EntityPlayer player, PlayerChunkViewerManager manager) 30 | { 31 | owner = player; 32 | this.manager = manager; 33 | 34 | PacketCustom packet = new PacketCustom(ChunkLoaderSPH.channel, 1); 35 | packet.sendToPlayer(player); 36 | 37 | for(WorldServer world : DimensionManager.getWorlds()) 38 | loadDimension(world); 39 | } 40 | 41 | public void writeTicketToPacket(PacketCustom packet, Ticket ticket, Collection chunkSet) 42 | { 43 | packet.writeInt(manager.ticketIDs.get(ticket)); 44 | packet.writeString(ticket.getModId()); 45 | String player = ticket.getPlayerName(); 46 | packet.writeBoolean(player != null); 47 | if(player != null) 48 | packet.writeString(player); 49 | packet.writeByte(ticket.getType().ordinal()); 50 | Entity entity = ticket.getEntity(); 51 | if(entity != null) 52 | packet.writeInt(entity.getEntityId()); 53 | packet.writeShort(chunkSet.size()); 54 | for(ChunkCoordIntPair chunk : chunkSet) 55 | { 56 | packet.writeInt(chunk.chunkXPos); 57 | packet.writeInt(chunk.chunkZPos); 58 | } 59 | 60 | knownTickets.add(manager.ticketIDs.get(ticket)); 61 | } 62 | 63 | @SuppressWarnings("unchecked") 64 | public void loadDimension(WorldServer world) 65 | { 66 | PacketCustom packet = new PacketCustom(channel, 2).compress(); 67 | int dim = CommonUtils.getDimension(world); 68 | packet.writeInt(dim); 69 | 70 | List allchunks = world.theChunkProviderServer.loadedChunks; 71 | packet.writeInt(allchunks.size()); 72 | for(Chunk chunk : allchunks) 73 | { 74 | packet.writeInt(chunk.xPosition); 75 | packet.writeInt(chunk.zPosition); 76 | } 77 | 78 | Map> tickets = ForgeChunkManager.getPersistentChunksFor(world).inverse().asMap(); 79 | packet.writeInt(tickets.size()); 80 | for(Entry> entry : tickets.entrySet()) 81 | writeTicketToPacket(packet, entry.getKey(), entry.getValue()); 82 | 83 | packet.sendToPlayer(owner); 84 | } 85 | 86 | public void unloadDimension(int dim) 87 | { 88 | PacketCustom packet = new PacketCustom(channel, 3); 89 | packet.writeInt(dim); 90 | 91 | packet.sendToPlayer(owner); 92 | } 93 | 94 | public void sendChunkChange(ChunkChange change) 95 | { 96 | PacketCustom packet = new PacketCustom(channel, 4); 97 | packet.writeInt(change.dimension); 98 | packet.writeInt(change.chunk.chunkXPos); 99 | packet.writeInt(change.chunk.chunkZPos); 100 | packet.writeBoolean(change.add); 101 | 102 | packet.sendToPlayer(owner); 103 | } 104 | 105 | public void sendTicketChange(TicketChange change) 106 | { 107 | int ticketID = manager.ticketIDs.get(change.ticket); 108 | if(!knownTickets.contains(ticketID)) 109 | addTicket(change.dimension, change.ticket); 110 | 111 | PacketCustom packet = new PacketCustom(channel, 5); 112 | packet.writeInt(change.dimension); 113 | packet.writeInt(ticketID); 114 | packet.writeInt(change.chunk.chunkXPos); 115 | packet.writeInt(change.chunk.chunkZPos); 116 | packet.writeBoolean(change.force); 117 | 118 | packet.sendToPlayer(owner); 119 | } 120 | 121 | public void updatePlayer(EntityPlayer player) 122 | { 123 | PacketCustom packet = new PacketCustom(channel, 6); 124 | packet.writeString(player.getCommandSenderName()); 125 | packet.writeInt(player.dimension); 126 | Vector3 pos = Vector3.fromEntity(player); 127 | packet.writeFloat((float) pos.x); 128 | packet.writeFloat((float) pos.y); 129 | packet.writeFloat((float) pos.z); 130 | 131 | packet.sendToPlayer(owner); 132 | } 133 | 134 | public void removePlayer(String username) 135 | { 136 | PacketCustom packet = new PacketCustom(channel, 7); 137 | packet.writeString(username); 138 | 139 | packet.sendToPlayer(owner); 140 | } 141 | 142 | public void addTicket(int dimension, Ticket ticket) 143 | { 144 | PacketCustom packet = new PacketCustom(channel, 8); 145 | packet.writeInt(dimension); 146 | writeTicketToPacket(packet, ticket, ticket.getChunkList()); 147 | 148 | packet.sendToPlayer(owner); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/PlayerChunkViewerManager.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.LinkedList; 8 | import net.minecraft.world.chunk.Chunk; 9 | import net.minecraft.world.ChunkCoordIntPair; 10 | import net.minecraft.entity.player.EntityPlayer; 11 | import net.minecraft.world.WorldServer; 12 | import net.minecraftforge.common.DimensionManager; 13 | import net.minecraftforge.common.ForgeChunkManager.Ticket; 14 | import codechicken.core.CommonUtils; 15 | import codechicken.core.ServerUtils; 16 | 17 | public class PlayerChunkViewerManager 18 | { 19 | public static class TicketChange 20 | { 21 | public final Ticket ticket; 22 | public final ChunkCoordIntPair chunk; 23 | public final boolean force; 24 | public final int dimension; 25 | 26 | public TicketChange(Ticket ticket, ChunkCoordIntPair chunk, boolean force) 27 | { 28 | this.ticket = ticket; 29 | this.dimension = CommonUtils.getDimension(ticket.world); 30 | this.chunk = chunk; 31 | this.force = force; 32 | } 33 | } 34 | 35 | public static class ChunkChange 36 | { 37 | public final ChunkCoordIntPair chunk; 38 | public final boolean add; 39 | public final int dimension; 40 | 41 | public ChunkChange(int dimension, ChunkCoordIntPair chunk, boolean add) 42 | { 43 | this.dimension = dimension; 44 | this.chunk = chunk; 45 | this.add = add; 46 | } 47 | } 48 | 49 | public static class DimensionChange 50 | { 51 | public final WorldServer world; 52 | public final boolean add; 53 | 54 | public DimensionChange(WorldServer world, boolean add) 55 | { 56 | this.world = world; 57 | this.add = add; 58 | } 59 | } 60 | 61 | public LinkedList playerViewers = new LinkedList(); 62 | public HashMap ticketIDs = new HashMap(); 63 | private int ticketID = 0; 64 | private int time = 0; 65 | //for tracking chunk changes 66 | private HashMap> lastLoadedChunkMap = new HashMap>(); 67 | //changes to be processed 68 | public LinkedList chunkChanges = new LinkedList(); 69 | public LinkedList ticketChanges = new LinkedList(); 70 | public LinkedList dimChanges = new LinkedList(); 71 | public LinkedList logouts = new LinkedList(); 72 | public LinkedList addViewers = new LinkedList(); 73 | 74 | private static PlayerChunkViewerManager instance; 75 | public static PlayerChunkViewerManager instance() 76 | { 77 | if(instance == null) 78 | instance = new PlayerChunkViewerManager(); 79 | return instance; 80 | } 81 | 82 | public void update() 83 | { 84 | time++; 85 | for(String username : logouts) 86 | for(Iterator iterator = playerViewers.iterator(); iterator.hasNext();) 87 | if(iterator.next().owner.getCommandSenderName().equals(username)) 88 | iterator.remove(); 89 | 90 | for(String username : logouts) 91 | for(PlayerChunkViewerTracker tracker : playerViewers) 92 | tracker.removePlayer(username); 93 | 94 | for(DimensionChange change : dimChanges) 95 | if(change.add) 96 | for(PlayerChunkViewerTracker tracker : playerViewers) 97 | tracker.loadDimension(change.world); 98 | 99 | for(ChunkChange change : chunkChanges) 100 | for(PlayerChunkViewerTracker tracker : playerViewers) 101 | tracker.sendChunkChange(change); 102 | 103 | for(TicketChange change : ticketChanges) 104 | { 105 | if(ticketIDs.containsKey(change.ticket)) 106 | { 107 | for(PlayerChunkViewerTracker tracker : playerViewers) 108 | tracker.sendTicketChange(change); 109 | } 110 | else 111 | { 112 | ticketIDs.put(change.ticket, ticketID++); 113 | for(PlayerChunkViewerTracker tracker : playerViewers) 114 | tracker.addTicket(CommonUtils.getDimension(change.ticket.world), change.ticket); 115 | } 116 | } 117 | 118 | for(DimensionChange change : dimChanges) 119 | if(!change.add) 120 | for(PlayerChunkViewerTracker tracker : playerViewers) 121 | tracker.unloadDimension(CommonUtils.getDimension(change.world)); 122 | 123 | if(time % 10 == 0) 124 | { 125 | for(EntityPlayer player : ServerUtils.getPlayers()) 126 | for(PlayerChunkViewerTracker tracker : playerViewers) 127 | tracker.updatePlayer(player); 128 | } 129 | 130 | for(String username : addViewers) 131 | { 132 | EntityPlayer player = ServerUtils.getPlayer(username); 133 | if(player == null) 134 | continue; 135 | 136 | if(playerViewers.isEmpty()) 137 | updateChunkChangeMap(); 138 | 139 | playerViewers.add(new PlayerChunkViewerTracker(player, this)); 140 | } 141 | 142 | addViewers.clear(); 143 | dimChanges.clear(); 144 | logouts.clear(); 145 | chunkChanges.clear(); 146 | ticketChanges.clear(); 147 | } 148 | 149 | @SuppressWarnings("unchecked") 150 | private void updateChunkChangeMap() 151 | { 152 | for(WorldServer world : DimensionManager.getWorlds()) 153 | { 154 | HashSet allChunks = new HashSet(); 155 | ArrayList loadedChunkCopy = new ArrayList(world.theChunkProviderServer.loadedChunks); 156 | for(Chunk chunk : loadedChunkCopy) 157 | allChunks.add(chunk.getChunkCoordIntPair()); 158 | 159 | lastLoadedChunkMap.put(CommonUtils.getDimension(world), allChunks); 160 | } 161 | } 162 | 163 | @SuppressWarnings("unchecked") 164 | public void calculateChunkChanges(WorldServer world) 165 | { 166 | if(playerViewers.isEmpty()) 167 | return; 168 | 169 | int dimension = CommonUtils.getDimension(world); 170 | HashSet wasLoadedChunks = lastLoadedChunkMap.get(dimension); 171 | if(wasLoadedChunks == null) 172 | wasLoadedChunks = new HashSet(); 173 | 174 | HashSet allChunks = new HashSet(); 175 | ArrayList loadedChunkCopy = new ArrayList(world.theChunkProviderServer.loadedChunks); 176 | for(Chunk chunk : loadedChunkCopy) 177 | { 178 | ChunkCoordIntPair coord = chunk.getChunkCoordIntPair(); 179 | allChunks.add(coord); 180 | if(!wasLoadedChunks.remove(coord)) 181 | chunkChanges.add(new ChunkChange(dimension, coord, true)); 182 | } 183 | 184 | for(ChunkCoordIntPair coord : wasLoadedChunks) 185 | chunkChanges.add(new ChunkChange(dimension, coord, false)); 186 | 187 | lastLoadedChunkMap.put(dimension, allChunks); 188 | } 189 | 190 | public boolean isViewerOpen(String username) 191 | { 192 | for(PlayerChunkViewerTracker tracker : playerViewers) 193 | if(tracker.owner.getCommandSenderName().equals(username)) 194 | return true; 195 | 196 | return false; 197 | } 198 | 199 | public static void serverShutdown() 200 | { 201 | instance = null; 202 | } 203 | 204 | public void closeViewer(String username) 205 | { 206 | for(Iterator iterator = playerViewers.iterator(); iterator.hasNext();) 207 | if(iterator.next().owner.getCommandSenderName().equals(username)) 208 | iterator.remove(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/TileChunkLoaderRenderer.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.awt.geom.Line2D; 4 | import java.awt.geom.Point2D; 5 | import java.util.Collection; 6 | import net.minecraft.util.AxisAlignedBB; 7 | import net.minecraft.world.ChunkCoordIntPair; 8 | import net.minecraft.client.renderer.entity.Render; 9 | import net.minecraft.tileentity.TileEntity; 10 | import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; 11 | import org.lwjgl.opengl.GL11; 12 | 13 | import codechicken.core.ClientUtils; 14 | import codechicken.lib.render.CCModelLibrary; 15 | import codechicken.lib.render.CCRenderState; 16 | import codechicken.lib.vec.Matrix4; 17 | import codechicken.lib.vec.Rotation; 18 | import codechicken.lib.vec.Vector3; 19 | 20 | public class TileChunkLoaderRenderer extends TileEntitySpecialRenderer 21 | { 22 | public static class RenderInfo 23 | { 24 | int activationCounter; 25 | boolean showLasers; 26 | 27 | public void update(TileChunkLoaderBase chunkLoader) 28 | { 29 | if(activationCounter < 20 && chunkLoader.active) 30 | activationCounter++; 31 | else if(activationCounter > 0 && !chunkLoader.active) 32 | activationCounter--; 33 | } 34 | } 35 | 36 | @Override 37 | public void renderTileEntityAt(TileEntity tile, double d, double d1, double d2, float f) 38 | { 39 | CCRenderState.reset(); 40 | CCRenderState.setBrightness(tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord); 41 | 42 | double rot = ClientUtils.getRenderTime()*2; 43 | double height; 44 | double size; 45 | double updown = (ClientUtils.getRenderTime()%50) / 25F; 46 | 47 | updown = (float) Math.sin(updown*3.141593); 48 | updown *= 0.2; 49 | 50 | TileChunkLoaderBase chunkLoader = (TileChunkLoaderBase)tile; 51 | if(chunkLoader instanceof TileChunkLoader) 52 | { 53 | TileChunkLoader ctile = (TileChunkLoader)chunkLoader; 54 | rot /= Math.pow(ctile.radius, 0.2); 55 | height = 0.9; 56 | size = 0.08; 57 | } 58 | else if(chunkLoader instanceof TileSpotLoader) 59 | { 60 | height = 0.5; 61 | size = 0.05; 62 | } 63 | else 64 | return; 65 | 66 | RenderInfo renderInfo = chunkLoader.renderInfo; 67 | double active = (renderInfo.activationCounter)/20D; 68 | if(chunkLoader.active && renderInfo.activationCounter < 20) 69 | active += f/20D; 70 | else if(!chunkLoader.active && renderInfo.activationCounter > 0) 71 | active -= f/20D; 72 | 73 | if(renderInfo.showLasers) 74 | { 75 | GL11.glDisable(GL11.GL_TEXTURE_2D); 76 | GL11.glDisable(GL11.GL_LIGHTING); 77 | GL11.glDisable(GL11.GL_FOG); 78 | drawRays(d, d1, d2, rot, updown, tile.xCoord, tile.yCoord, tile.zCoord, chunkLoader.getChunks()); 79 | GL11.glEnable(GL11.GL_TEXTURE_2D); 80 | GL11.glEnable(GL11.GL_LIGHTING); 81 | GL11.glEnable(GL11.GL_FOG); 82 | } 83 | rot = ClientUtils.getRenderTime()*active / 3F; 84 | 85 | Matrix4 pearlMat = CCModelLibrary.getRenderMatrix( 86 | new Vector3(d+0.5, d1+height+(updown + 0.3)*active, d2+0.5), 87 | new Rotation(rot, new Vector3(0, 1, 0)), 88 | size); 89 | 90 | GL11.glDisable(GL11.GL_LIGHTING); 91 | CCRenderState.changeTexture("chickenchunks:textures/hedronmap.png"); 92 | CCRenderState.startDrawing(4); 93 | CCModelLibrary.icosahedron4.render(pearlMat); 94 | CCRenderState.draw(); 95 | GL11.glEnable(GL11.GL_LIGHTING); 96 | } 97 | 98 | public Point2D.Double findIntersection(Line2D line1, Line2D line2) 99 | { 100 | // calculate differences 101 | double xD1 = line1.getX2() - line1.getX1(); 102 | double yD1 = line1.getY2() - line1.getY1(); 103 | double xD2 = line2.getX2() - line2.getX1(); 104 | double yD2 = line2.getY2() - line2.getY1(); 105 | 106 | double xD3 = line1.getX1() - line2.getX1(); 107 | double yD3 = line1.getY1() - line2.getY1(); 108 | 109 | // find intersection Pt between two lines 110 | Point2D.Double pt = new Point2D.Double(0, 0); 111 | double div = yD2 * xD1 - xD2 * yD1; 112 | if(div == 0)//lines are parallel 113 | return null; 114 | double ua = (xD2 * yD3 - yD2 * xD3) / div; 115 | pt.x = line1.getX1() + ua * xD1; 116 | pt.y = line1.getY1() + ua * yD1; 117 | 118 | if(ptOnLineInSegment(pt, line1) && ptOnLineInSegment(pt, line2)) 119 | return pt; 120 | 121 | return null; 122 | } 123 | 124 | public boolean ptOnLineInSegment(Point2D point, Line2D line) 125 | { 126 | return point.getX() >= Math.min(line.getX1(), line.getX2()) && 127 | point.getX() <= Math.max(line.getX1(), line.getX2()) && 128 | point.getY() >= Math.min(line.getY1(), line.getY2()) && 129 | point.getY() <= Math.max(line.getY1(), line.getY2()); 130 | } 131 | 132 | public void drawRays(double d, double d1, double d2, double rotationAngle, double updown, int x, int y, int z, Collection chunkSet) 133 | { 134 | int cx = (x >> 4) << 4; 135 | int cz = (z >> 4) << 4; 136 | 137 | GL11.glPushMatrix(); 138 | GL11.glTranslated(d+cx-x+8, d1 + updown + 2, d2+cz-z+8); 139 | GL11.glRotatef((float) rotationAngle, 0, 1, 0); 140 | 141 | double[] distances = new double[4]; 142 | 143 | Point2D.Double center = new Point2D.Double(cx+8, cz+8); 144 | 145 | final int[][] coords = new int[][]{{0,0},{16,0},{16,16},{0,16}}; 146 | 147 | Point2D.Double[] absRays = new Point2D.Double[4]; 148 | 149 | for(int ray = 0; ray < 4; ray++) 150 | { 151 | double rayAngle = Math.toRadians(rotationAngle+90*ray); 152 | absRays[ray] = new Point2D.Double(Math.sin(rayAngle), Math.cos(rayAngle)); 153 | } 154 | 155 | Line2D.Double[] rays = new Line2D.Double[]{ 156 | new Line2D.Double(center.x, center.y,center.x+1600*absRays[0].x,center.y+1600*absRays[0].y), 157 | new Line2D.Double(center.x, center.y,center.x+1600*absRays[1].x,center.y+1600*absRays[1].y), 158 | new Line2D.Double(center.x, center.y,center.x+1600*absRays[2].x,center.y+1600*absRays[2].y), 159 | new Line2D.Double(center.x, center.y,center.x+1600*absRays[3].x,center.y+1600*absRays[3].y)}; 160 | 161 | for(ChunkCoordIntPair pair : chunkSet) 162 | { 163 | int chunkBlockX = pair.chunkXPos<<4; 164 | int chunkBlockZ = pair.chunkZPos<<4; 165 | for(int side = 0; side < 4; side++) 166 | { 167 | int[] offset1 = coords[side]; 168 | int[] offset2 = coords[(side+1)%4]; 169 | Line2D.Double line1 = new Line2D.Double(chunkBlockX+offset1[0], chunkBlockZ+offset1[1], chunkBlockX+offset2[0], chunkBlockZ+offset2[1]); 170 | for(int ray = 0; ray < 4; ray++) 171 | { 172 | Point2D.Double isct = findIntersection(line1, rays[ray]); 173 | if(isct == null) 174 | continue; 175 | 176 | isct.setLocation(isct.x-center.x, isct.y-center.y); 177 | 178 | double lenPow2 = isct.x*isct.x+isct.y*isct.y; 179 | if(lenPow2 > distances[ray]) 180 | distances[ray] = lenPow2; 181 | } 182 | } 183 | } 184 | 185 | GL11.glColor4d(0.9, 0, 0, 1); 186 | for(int ray = 0; ray < 4; ray++) 187 | { 188 | distances[ray] = Math.sqrt(distances[ray]); 189 | GL11.glRotatef(90, 0, 1, 0); 190 | Render.renderAABB(AxisAlignedBB.getBoundingBox(0, -0.05, -0.05, distances[ray], 0.05, 0.05)); 191 | } 192 | GL11.glPopMatrix(); 193 | 194 | GL11.glPushMatrix(); 195 | GL11.glTranslated(d+cx-x+8, d1-y, d2+cz-z+8); 196 | for(int ray = 0; ray < 4; ray++) 197 | { 198 | GL11.glPushMatrix(); 199 | GL11.glTranslated(absRays[ray].x*distances[ray], 0, absRays[ray].y*distances[ray]); 200 | Render.renderAABB(AxisAlignedBB.getBoundingBox(-0.05, 0, -0.05, 0.05, 256, 0.05)); 201 | GL11.glPopMatrix(); 202 | } 203 | GL11.glPopMatrix(); 204 | 205 | double toCenter = Math.sqrt((cx+7.5-x)*(cx+7.5-x)+0.8*0.8+(cz+7.5-z)*(cz+7.5-z)); 206 | 207 | GL11.glPushMatrix(); 208 | GL11.glColor4d(0, 0.9, 0, 1); 209 | GL11.glTranslated(d+0.5, d1+1.2+updown, d2+0.5); 210 | GL11.glRotatef((float) (Math.atan2((cx+7.5-x), (cz+7.5-z))*180/3.1415)+90, 0, 1, 0); 211 | GL11.glRotatef((float) (-Math.asin(0.8/toCenter)*180/3.1415), 0, 0, 1); 212 | Render.renderAABB(AxisAlignedBB.getBoundingBox(-toCenter, -0.03, -0.03, 0, 0.03, 0.03)); 213 | GL11.glPopMatrix(); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/PlayerChunkViewer.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.awt.Color; 4 | import java.awt.Component; 5 | import java.awt.Container; 6 | import java.awt.Dimension; 7 | import java.awt.Graphics; 8 | import java.awt.Graphics2D; 9 | import java.awt.GraphicsEnvironment; 10 | import java.awt.LayoutManager; 11 | import java.awt.Point; 12 | import java.awt.Rectangle; 13 | import java.awt.event.ActionEvent; 14 | import java.awt.event.ActionListener; 15 | import java.awt.event.MouseEvent; 16 | import java.awt.event.MouseListener; 17 | import java.awt.event.MouseMotionListener; 18 | import java.awt.event.WindowAdapter; 19 | import java.awt.event.WindowEvent; 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.LinkedList; 24 | import java.util.Set; 25 | 26 | import javax.swing.JComboBox; 27 | import javax.swing.JDialog; 28 | import javax.swing.JFrame; 29 | import javax.swing.JLabel; 30 | import javax.swing.JPanel; 31 | import javax.swing.JScrollPane; 32 | import javax.swing.JTextField; 33 | import javax.swing.JTextPane; 34 | import javax.swing.ScrollPaneConstants; 35 | import javax.swing.SwingUtilities; 36 | import javax.swing.ToolTipManager; 37 | 38 | import net.minecraft.world.ChunkCoordIntPair; 39 | import net.minecraft.entity.Entity; 40 | import net.minecraft.entity.EntityList; 41 | import net.minecraft.client.Minecraft; 42 | import net.minecraft.client.gui.GuiMainMenu; 43 | import net.minecraft.client.multiplayer.WorldClient; 44 | import codechicken.lib.packet.PacketCustom; 45 | import codechicken.lib.vec.Vector3; 46 | 47 | @SuppressWarnings("serial") 48 | public class PlayerChunkViewer extends JFrame 49 | { 50 | public static class TicketInfo 51 | { 52 | int ID; 53 | String modId; 54 | String player; 55 | net.minecraftforge.common.ForgeChunkManager.Type type; 56 | Entity entity; 57 | Set chunkSet; 58 | 59 | public TicketInfo(PacketCustom packet, WorldClient world) 60 | { 61 | ID = packet.readInt(); 62 | modId = packet.readString(); 63 | if(packet.readBoolean()) 64 | player = packet.readString(); 65 | type = net.minecraftforge.common.ForgeChunkManager.Type.values()[packet.readUByte()]; 66 | if(type == net.minecraftforge.common.ForgeChunkManager.Type.ENTITY) 67 | entity = world.getEntityByID(packet.readInt()); 68 | int chunks = packet.readUShort(); 69 | chunkSet = new HashSet(chunks); 70 | for(int i = 0; i < chunks; i++) 71 | { 72 | chunkSet.add(new ChunkCoordIntPair(packet.readInt(), packet.readInt())); 73 | } 74 | } 75 | } 76 | 77 | public static class PlayerInfo 78 | { 79 | public PlayerInfo(String username2) 80 | { 81 | this.username = username2; 82 | } 83 | 84 | final String username; 85 | Vector3 position; 86 | int dimension; 87 | } 88 | 89 | public static class DimensionChunkInfo 90 | { 91 | public final int dimension; 92 | public HashSet allchunks = new HashSet(); 93 | public HashMap tickets = new HashMap(); 94 | 95 | public DimensionChunkInfo(int dim) 96 | { 97 | dimension = dim; 98 | } 99 | } 100 | 101 | public class TicketInfoDialog extends JDialog implements LayoutManager 102 | { 103 | private LinkedList tickets; 104 | 105 | private JTextPane infoPane; 106 | private JScrollPane infoScrollPane; 107 | private JTextPane chunkPane; 108 | private JScrollPane chunkScrollPane; 109 | private JComboBox ticketComboBox; 110 | 111 | public TicketInfoDialog(LinkedList tickets) 112 | { 113 | super(PlayerChunkViewer.this); 114 | setModalityType(ModalityType.DOCUMENT_MODAL); 115 | this.tickets = tickets; 116 | 117 | infoPane = new JTextPane(); 118 | infoPane.setEditable(false); 119 | infoPane.setOpaque(false); 120 | infoPane.setContentType("text/html"); 121 | 122 | infoScrollPane = new JScrollPane(infoPane); 123 | infoScrollPane.setOpaque(false); 124 | add(infoScrollPane); 125 | 126 | chunkPane = new JTextPane(); 127 | chunkPane.setEditable(false); 128 | chunkPane.setOpaque(false); 129 | chunkPane.setContentType("text/html"); 130 | 131 | chunkScrollPane = new JScrollPane(chunkPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 132 | add(chunkScrollPane); 133 | 134 | ticketComboBox = new JComboBox(); 135 | for(TicketInfo ticket : tickets) 136 | { 137 | String ident = ticket.modId; 138 | if(ticket.player != null) 139 | ident += ", " + ticket.player; 140 | ident += " #" + ticket.ID; 141 | ticketComboBox.addItem(ident); 142 | } 143 | add(ticketComboBox); 144 | 145 | addWindowListener(new WindowAdapter() 146 | { 147 | @Override 148 | public void windowClosing(WindowEvent e) 149 | { 150 | dialog = null; 151 | } 152 | }); 153 | 154 | setLayout(this); 155 | setSize(getPreferredSize()); 156 | setLocationRelativeTo(null); 157 | pack(); 158 | 159 | dialog = this; 160 | 161 | setVisible(true); 162 | } 163 | 164 | public void update() 165 | { 166 | TicketInfo ticket = tickets.get(ticketComboBox.getSelectedIndex()); 167 | String info = ""; 168 | info += "Mod: " + ticket.modId; 169 | if(ticket.player != null) 170 | info += "
Player: " + ticket.player; 171 | info += "
Type: " + ticket.type.name(); 172 | if(ticket.entity != null) 173 | info += "
Entity: " + EntityList.classToStringMapping.get(ticket.entity) + "#" + ticket.entity.getEntityId() + " (" + String.format("%.2f", ticket.entity.posX) + ", " + String.format("%.2f", ticket.entity.posY) + ", " + String.format("%.2f", ticket.entity.posZ) + ")"; 174 | info+="

ForcedChunks

"; 175 | String chunks = ""; 176 | for(ChunkCoordIntPair coord : ticket.chunkSet) 177 | chunks += coord.chunkXPos + ", " + coord.chunkZPos + "
"; 178 | chunks += "
"; 179 | infoPane.setText(info); 180 | chunkPane.setText(chunks); 181 | repaint(); 182 | } 183 | 184 | @Override 185 | public void layoutContainer(Container parent) 186 | { 187 | Dimension size = parent.getSize(); 188 | ticketComboBox.setBounds(40, 20, size.width - 80, 20); 189 | int w = size.width - 40; 190 | int y = 60; 191 | infoPane.setBounds(5, 5, w-10, 5); 192 | chunkPane.setBounds(5, 5, w-10, 5); 193 | infoScrollPane.setBounds(20, y, w, Math.min(size.height - 80, infoPane.getPreferredSize().height+10)); 194 | y += 10+infoScrollPane.getHeight(); 195 | chunkScrollPane.setBounds(20, y, w, Math.max(0, size.height-40-y)); 196 | } 197 | 198 | @Override 199 | public void addLayoutComponent(String name, Component comp) 200 | { 201 | } 202 | 203 | @Override 204 | public Dimension minimumLayoutSize(Container parent) 205 | { 206 | return new Dimension(250, 300); 207 | } 208 | 209 | @Override 210 | public Dimension preferredLayoutSize(Container parent) 211 | { 212 | return new Dimension(250, 300); 213 | } 214 | 215 | @Override 216 | public void removeLayoutComponent(Component comp) 217 | { 218 | } 219 | } 220 | 221 | private HashMap players = new HashMap(); 222 | private final HashMap dimensionChunks = new HashMap(); 223 | 224 | private int dimension = 0; 225 | private int xCenter = 0; 226 | private int zCenter = 0; 227 | private DisplayArea displayArea; 228 | private JComboBox dimComboBox; 229 | private JLabel dimLabel; 230 | private JTextField xArea; 231 | private JLabel xLabel; 232 | private JTextField zArea; 233 | private JLabel zLabel; 234 | public TicketInfoDialog dialog; 235 | 236 | public static void openViewer(int x, int z, int dim) 237 | { 238 | instance = new PlayerChunkViewer(x, z, dim); 239 | } 240 | 241 | private PlayerChunkViewer(int x, int z, int dim) 242 | { 243 | addWindowListener(new WindowAdapter() 244 | { 245 | @Override 246 | public void windowClosing(WindowEvent e) 247 | { 248 | if(Minecraft.getMinecraft().getNetHandler() != null) 249 | ChunkLoaderCPH.sendGuiClosing(); 250 | } 251 | }); 252 | 253 | setLayout(new LayoutManager() 254 | { 255 | @Override 256 | public void removeLayoutComponent(Component paramComponent) 257 | { 258 | } 259 | 260 | @Override 261 | public Dimension preferredLayoutSize(Container paramContainer) 262 | { 263 | return new Dimension(500, 500); 264 | } 265 | 266 | @Override 267 | public Dimension minimumLayoutSize(Container paramContainer) 268 | { 269 | return null; 270 | } 271 | 272 | @Override 273 | public void layoutContainer(Container paramContainer) 274 | { 275 | int width = getRootPane().getWidth(); 276 | int height = getRootPane().getHeight(); 277 | 278 | xLabel.setBounds(20, 10, 50, 20); 279 | xArea.setBounds(70, 10, 60, 20); 280 | zLabel.setBounds(140, 10, 50, 20); 281 | zArea.setBounds(190, 10, 60, 20); 282 | dimLabel.setBounds(260, 10, 60, 20); 283 | dimComboBox.setBounds(330, 10, 70, 20); 284 | displayArea.setBounds(0, 40, width, height - 40); 285 | } 286 | 287 | @Override 288 | public void addLayoutComponent(String paramString, Component paramComponent) 289 | { 290 | } 291 | }); 292 | 293 | ToolTipManager.sharedInstance().setEnabled(true); 294 | ToolTipManager.sharedInstance().setInitialDelay(0); 295 | ToolTipManager.sharedInstance().setReshowDelay(100); 296 | addComponents(); 297 | pack(); 298 | setTitle("Chunk Viewer"); 299 | 300 | Point p = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); 301 | int width = 500; 302 | int height = 500; 303 | setCenter(x, z); 304 | dimension = dim; 305 | setBounds(p.x - width / 2, p.y - height / 2, width, height); 306 | 307 | setBackground(new Color(1F, 1, 1)); 308 | 309 | setVisible(true); 310 | 311 | SwingUtilities.invokeLater(new Runnable() 312 | { 313 | @Override 314 | public void run() 315 | { 316 | startUpdateThread(); 317 | } 318 | }); 319 | } 320 | 321 | protected void startUpdateThread() 322 | { 323 | new Thread("Info Frame Update Thread") 324 | { 325 | public void run() 326 | { 327 | while(true) 328 | { 329 | if(Minecraft.getMinecraft().currentScreen instanceof GuiMainMenu) 330 | dispose(); 331 | 332 | if(instance == null || !isVisible()) 333 | return; 334 | 335 | update(); 336 | if(dialog != null) 337 | dialog.update(); 338 | 339 | try 340 | { 341 | Thread.sleep(50); 342 | } 343 | catch(InterruptedException e) 344 | { 345 | } 346 | } 347 | } 348 | }.start(); 349 | } 350 | 351 | protected void update() 352 | { 353 | int selectedDim = dimension; 354 | boolean needsReset = false; 355 | LinkedList dims = new LinkedList(dimensionChunks.keySet()); 356 | Collections.sort(dims); 357 | if(dims.size() != dimComboBox.getItemCount()) 358 | needsReset = true; 359 | else 360 | { 361 | for(int index = 0; index < dimComboBox.getItemCount();) 362 | { 363 | if(!dims.get(index).equals(dimComboBox.getItemAt(index))) 364 | { 365 | needsReset = true; 366 | break; 367 | } 368 | index++; 369 | } 370 | } 371 | 372 | if(needsReset) 373 | { 374 | dimComboBox.removeAllItems(); 375 | dims = new LinkedList(dimensionChunks.keySet()); 376 | Collections.sort(dims); 377 | for(int dim : dims) 378 | { 379 | dimComboBox.addItem(dim); 380 | } 381 | 382 | if(dims.contains(selectedDim)) 383 | dimComboBox.setSelectedItem(selectedDim); 384 | } 385 | repaint(); 386 | } 387 | 388 | private void addComponents() 389 | { 390 | add(getDisplayArea()); 391 | add(getXLabel()); 392 | add(getXArea()); 393 | add(getZLabel()); 394 | add(getZArea()); 395 | add(getDimLabel()); 396 | add(getDimComboBox()); 397 | } 398 | 399 | public DisplayArea getDisplayArea() 400 | { 401 | if(displayArea == null) 402 | { 403 | displayArea = new DisplayArea(); 404 | displayArea.addMouseListener(displayArea); 405 | displayArea.addMouseMotionListener(displayArea); 406 | } 407 | return displayArea; 408 | } 409 | 410 | public JLabel getXLabel() 411 | { 412 | if(xLabel == null) 413 | { 414 | xLabel = new JLabel("xCenter"); 415 | } 416 | return xLabel; 417 | } 418 | 419 | public JTextField getXArea() 420 | { 421 | if(xArea == null) 422 | { 423 | xArea = new JTextField(); 424 | xArea.addActionListener(new ActionListener() 425 | { 426 | @Override 427 | public void actionPerformed(ActionEvent e) 428 | { 429 | try { 430 | setCenter(Integer.parseInt(xArea.getText()), zCenter); 431 | } catch (NumberFormatException ignored) { 432 | } 433 | } 434 | }); 435 | } 436 | return xArea; 437 | } 438 | 439 | public JLabel getZLabel() 440 | { 441 | if(zLabel == null) 442 | { 443 | zLabel = new JLabel("zCenter"); 444 | } 445 | return zLabel; 446 | } 447 | 448 | public JTextField getZArea() 449 | { 450 | if(zArea == null) 451 | { 452 | zArea = new JTextField(); 453 | zArea.addActionListener(new ActionListener() 454 | { 455 | @Override 456 | public void actionPerformed(ActionEvent e) 457 | { 458 | try { 459 | setCenter(xCenter, Integer.parseInt(zArea.getText())); 460 | } catch (NumberFormatException ignored) { 461 | } 462 | } 463 | }); 464 | } 465 | return zArea; 466 | } 467 | 468 | public JLabel getDimLabel() 469 | { 470 | if(dimLabel == null) 471 | { 472 | dimLabel = new JLabel("Dimension"); 473 | } 474 | return dimLabel; 475 | } 476 | 477 | public JComboBox getDimComboBox() 478 | { 479 | if(dimComboBox == null) 480 | { 481 | dimComboBox = new JComboBox(); 482 | dimComboBox.addActionListener(new ActionListener() 483 | { 484 | @Override 485 | public void actionPerformed(ActionEvent e) 486 | { 487 | if(e.getActionCommand().equals("comboBoxChanged")) 488 | { 489 | if(dimComboBox.getSelectedItem() != null) 490 | dimension = (Integer) dimComboBox.getSelectedItem(); 491 | } 492 | } 493 | }); 494 | } 495 | return dimComboBox; 496 | } 497 | 498 | public class DisplayArea extends JPanel implements MouseListener, MouseMotionListener 499 | { 500 | int mouseClickedX; 501 | int centerClickedX; 502 | int mouseClickedY; 503 | int centerClickedZ; 504 | 505 | Point center; 506 | 507 | @Override 508 | public void paint(Graphics g1) 509 | { 510 | synchronized(dimensionChunks) 511 | { 512 | Graphics2D g = (Graphics2D) g1; 513 | Dimension dim = getSize(); 514 | g.clearRect(0, 0, dim.width, dim.height); 515 | 516 | center = new Point(dim.width / 2, dim.height / 2); 517 | DimensionChunkInfo dimInfo = dimensionChunks.get(dimension); 518 | if(dimInfo == null) 519 | { 520 | dimension = 0; 521 | return; 522 | } 523 | 524 | g.setColor(new Color(1F, 0, 0)); 525 | for(ChunkCoordIntPair coord : dimInfo.allchunks) 526 | { 527 | Point pos = getChunkRenderPosition(coord.chunkXPos, coord.chunkZPos); 528 | g.fillRect(pos.x, pos.y, 4, 4); 529 | } 530 | 531 | HashSet forcedChunks = new HashSet(); 532 | int numTickets = 0; 533 | for(TicketInfo ticket : dimInfo.tickets.values()) 534 | { 535 | if(!ticket.chunkSet.isEmpty()) 536 | numTickets++; 537 | for(ChunkCoordIntPair coord : ticket.chunkSet) 538 | forcedChunks.add(coord); 539 | } 540 | 541 | g.setColor(new Color(0, 1F, 0)); 542 | for(ChunkCoordIntPair coord : forcedChunks) 543 | { 544 | Point pos = getChunkRenderPosition(coord.chunkXPos, coord.chunkZPos); 545 | g.fillRect(pos.x + 1, pos.y + 1, 2, 2); 546 | } 547 | 548 | int numPlayers = 0; 549 | g.setColor(new Color(0, 0, 1F)); 550 | for(PlayerInfo info : players.values()) 551 | { 552 | if(info.dimension == dimension) 553 | { 554 | Point pos = getChunkRenderPosition((int) info.position.x, 0, (int) info.position.z); 555 | g.fillRect(pos.x + 1, pos.y + 1, 2, 2); 556 | numPlayers++; 557 | } 558 | } 559 | 560 | g.setColor(new Color(0, 0, 0)); 561 | for(int x = (xCenter >> 4) - (center.x >> 2) - 2; x < (xCenter >> 4) + (center.x >> 2) + 2; x++) 562 | { 563 | if(x % 16 == 0) 564 | { 565 | Point pos = getChunkRenderPosition(x, ((zCenter + 128) >> 8) << 4); 566 | g.drawLine(pos.x, 0, pos.x, dim.height); 567 | 568 | g.drawString(Integer.toString(x << 4), pos.x + 2, pos.y + 12); 569 | } 570 | } 571 | for(int z = (zCenter >> 4) - (center.y >> 2) - 2; z < (zCenter >> 4) + (center.y >> 2) + 2; z++) 572 | { 573 | if(z % 16 == 0) 574 | { 575 | Point pos = getChunkRenderPosition(((xCenter + 128) >> 8) << 4, z); 576 | g.drawLine(0, pos.y, dim.width, pos.y); 577 | 578 | g.drawString(Integer.toString(z << 4), pos.x + 2, pos.y - 2); 579 | } 580 | } 581 | 582 | g.setColor(new Color(1F, 1F, 1F)); 583 | g.fillRect(0, 0, 100, 60); 584 | 585 | g.setColor(new Color(0, 0, 0)); 586 | g.drawString("Tickets: " + numTickets, 10, 20); 587 | g.drawString("Forced Chunks: " + forcedChunks.size(), 10, 30); 588 | g.drawString("Chunks: " + dimInfo.allchunks.size(), 10, 40); 589 | g.drawString("Players: " + numPlayers, 10, 50); 590 | } 591 | } 592 | 593 | public Point getChunkRenderPosition(int chunkX, int chunkZ) 594 | { 595 | int relBlockX = (chunkX << 4) + 8 - xCenter; 596 | int relBlockZ = (chunkZ << 4) + 8 - zCenter; 597 | return new Point(center.x + (relBlockX >> 2), center.y + (relBlockZ >> 2)); 598 | } 599 | 600 | public Point getChunkRenderPosition(int blockX, int blockY, int blockZ) 601 | { 602 | return getChunkRenderPosition(blockX >> 4, blockZ >> 4); 603 | } 604 | 605 | @SuppressWarnings("unused") 606 | @Override 607 | public void mouseClicked(MouseEvent event) 608 | { 609 | if(event.getButton() != MouseEvent.BUTTON1 || event.getClickCount() < 2) 610 | return; 611 | 612 | Point mouse = event.getPoint(); 613 | DimensionChunkInfo dimInfo = dimensionChunks.get(dimension); 614 | if(dimInfo == null) 615 | { 616 | dimension = 0; 617 | return; 618 | } 619 | 620 | LinkedList mouseOverTickets = getTicketsUnderMouse(dimInfo, mouse); 621 | if(!mouseOverTickets.isEmpty()) 622 | new TicketInfoDialog(mouseOverTickets); 623 | } 624 | 625 | @Override 626 | public void mousePressed(MouseEvent event) 627 | { 628 | mouseClickedX = event.getX(); 629 | centerClickedX = xCenter; 630 | mouseClickedY = event.getY(); 631 | centerClickedZ = zCenter; 632 | } 633 | 634 | @Override 635 | public void mouseReleased(MouseEvent event) 636 | { 637 | } 638 | 639 | @Override 640 | public void mouseEntered(MouseEvent event) 641 | { 642 | } 643 | 644 | @Override 645 | public void mouseExited(MouseEvent event) 646 | { 647 | } 648 | 649 | @Override 650 | public void mouseDragged(MouseEvent event) 651 | { 652 | setCenter((mouseClickedX - event.getX()) * 4 + centerClickedX, (mouseClickedY - event.getY()) * 4 + centerClickedZ); 653 | } 654 | 655 | public LinkedList getTicketsUnderMouse(DimensionChunkInfo dimInfo, Point mouse) 656 | { 657 | LinkedList mouseOverTickets = new LinkedList(); 658 | for(TicketInfo ticket : dimInfo.tickets.values()) 659 | { 660 | for(ChunkCoordIntPair coord : ticket.chunkSet) 661 | { 662 | Point pos = getChunkRenderPosition(coord.chunkXPos, coord.chunkZPos); 663 | if(new Rectangle(pos.x, pos.y, 4, 4).contains(mouse)) 664 | { 665 | mouseOverTickets.add(ticket); 666 | } 667 | } 668 | } 669 | return mouseOverTickets; 670 | } 671 | 672 | @Override 673 | public void mouseMoved(MouseEvent event) 674 | { 675 | synchronized(dimensionChunks) 676 | { 677 | Point mouse = event.getPoint(); 678 | DimensionChunkInfo dimInfo = dimensionChunks.get(dimension); 679 | if(dimInfo == null) 680 | { 681 | dimension = 0; 682 | return; 683 | } 684 | String tip = ""; 685 | LinkedList mouseOverTickets = getTicketsUnderMouse(dimInfo, mouse); 686 | if(!mouseOverTickets.isEmpty()) 687 | { 688 | tip += mouseOverTickets.size() + (mouseOverTickets.size() == 1 ? " ticket" : " tickets"); 689 | for(TicketInfo info : mouseOverTickets) 690 | { 691 | tip += "\n" + info.modId; 692 | if(info.player != null) 693 | tip += ", " + info.player; 694 | } 695 | } 696 | 697 | for(PlayerInfo info : players.values()) 698 | { 699 | if(info.dimension == dimension) 700 | { 701 | Point pos = getChunkRenderPosition((int) info.position.x, 0, (int) info.position.z); 702 | if(new Rectangle(pos.x, pos.y, 4, 4).contains(mouse)) 703 | tip += "\n\n"+info.username + "\n(" + String.format("%.2f", info.position.x) + ", " + String.format("%.2f", info.position.y) + ", " + String.format("%.2f", info.position.z) + ")"; 704 | } 705 | } 706 | setToolTipText(tip.length() > 0 ? tip : null); 707 | } 708 | } 709 | 710 | @Override 711 | public void setToolTipText(String paramString) 712 | { 713 | if(paramString == null) 714 | super.setToolTipText(null); 715 | else 716 | super.setToolTipText("" + paramString.replace("\n", "
") + ""); 717 | } 718 | } 719 | 720 | private static PlayerChunkViewer instance; 721 | 722 | public static PlayerChunkViewer instance() 723 | { 724 | return instance; 725 | } 726 | 727 | public void setCenter(int blockX, int blockZ) 728 | { 729 | xArea.setText(Integer.toString(blockX)); 730 | xCenter = blockX; 731 | zArea.setText(Integer.toString(blockZ)); 732 | zCenter = blockZ; 733 | } 734 | 735 | public void loadDimension(PacketCustom packet, WorldClient world) 736 | { 737 | synchronized(dimensionChunks) 738 | { 739 | DimensionChunkInfo dimInfo = new DimensionChunkInfo(packet.readInt()); 740 | 741 | int numChunks = packet.readInt(); 742 | for(int i = 0; i < numChunks; i++) 743 | dimInfo.allchunks.add(new ChunkCoordIntPair(packet.readInt(), packet.readInt())); 744 | 745 | int numTickets = packet.readInt(); 746 | for(int i = 0; i < numTickets; i++) 747 | { 748 | TicketInfo ticket = new TicketInfo(packet, world); 749 | dimInfo.tickets.put(ticket.ID, ticket); 750 | } 751 | 752 | dimensionChunks.put(dimInfo.dimension, dimInfo); 753 | } 754 | } 755 | 756 | public void unloadDimension(int dim) 757 | { 758 | dimensionChunks.remove(dim); 759 | } 760 | 761 | public void handleChunkChange(int dimension, ChunkCoordIntPair coord, boolean add) 762 | { 763 | synchronized(dimensionChunks) 764 | { 765 | if(add) 766 | dimensionChunks.get(dimension).allchunks.add(coord); 767 | else 768 | dimensionChunks.get(dimension).allchunks.remove(coord); 769 | } 770 | } 771 | 772 | public void handleTicketChange(int dimension, int ticketID, ChunkCoordIntPair coord, boolean force) 773 | { 774 | synchronized(dimensionChunks) 775 | { 776 | DimensionChunkInfo dimInfo = dimensionChunks.get(dimension); 777 | TicketInfo ticket = dimInfo.tickets.get(ticketID); 778 | if(force) 779 | ticket.chunkSet.add(coord); 780 | else 781 | ticket.chunkSet.remove(coord); 782 | } 783 | } 784 | 785 | public void handleNewTicket(PacketCustom packet, WorldClient world) 786 | { 787 | synchronized(dimensionChunks) 788 | { 789 | int dim = packet.readInt(); 790 | TicketInfo ticket = new TicketInfo(packet, world); 791 | dimensionChunks.get(dim).tickets.put(ticket.ID, ticket); 792 | } 793 | } 794 | 795 | public void handlePlayerUpdate(String username, int dimension, Vector3 position) 796 | { 797 | synchronized(dimensionChunks) 798 | { 799 | PlayerInfo info = players.get(username); 800 | if(info == null) 801 | players.put(username, info = new PlayerInfo(username)); 802 | info.dimension = dimension; 803 | info.position = position; 804 | } 805 | } 806 | 807 | public void removePlayer(String username) 808 | { 809 | synchronized(dimensionChunks) 810 | { 811 | players.remove(username); 812 | } 813 | } 814 | } 815 | -------------------------------------------------------------------------------- /src/codechicken/chunkloader/ChunkLoaderManager.java: -------------------------------------------------------------------------------- 1 | package codechicken.chunkloader; 2 | 3 | import java.io.*; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Iterator; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Map.Entry; 12 | import java.util.Stack; 13 | 14 | import com.google.common.collect.ImmutableSetMultimap; 15 | import com.google.common.collect.LinkedListMultimap; 16 | import com.google.common.collect.ListMultimap; 17 | 18 | import codechicken.core.CommonUtils; 19 | import codechicken.core.ServerUtils; 20 | import codechicken.lib.config.ConfigFile; 21 | import codechicken.lib.config.ConfigTag; 22 | import codechicken.lib.vec.BlockCoord; 23 | import cpw.mods.fml.common.FMLCommonHandler; 24 | import cpw.mods.fml.common.Loader; 25 | import cpw.mods.fml.common.ModContainer; 26 | 27 | import net.minecraft.server.MinecraftServer; 28 | import net.minecraft.server.management.PlayerManager.PlayerInstance; 29 | import net.minecraft.world.chunk.Chunk; 30 | import net.minecraft.world.ChunkCoordIntPair; 31 | import net.minecraft.entity.player.EntityPlayer; 32 | import net.minecraft.entity.player.EntityPlayerMP; 33 | import net.minecraft.server.management.PlayerManager; 34 | import net.minecraft.tileentity.TileEntity; 35 | import net.minecraft.world.World; 36 | import net.minecraft.world.WorldServer; 37 | import net.minecraftforge.common.DimensionManager; 38 | import net.minecraftforge.common.ForgeChunkManager; 39 | import net.minecraftforge.common.ForgeChunkManager.OrderedLoadingCallback; 40 | import net.minecraftforge.common.ForgeChunkManager.PlayerOrderedLoadingCallback; 41 | import net.minecraftforge.common.ForgeChunkManager.Ticket; 42 | import net.minecraftforge.common.ForgeChunkManager.Type; 43 | import org.apache.logging.log4j.LogManager; 44 | 45 | public class ChunkLoaderManager 46 | { 47 | private static class DimChunkCoord 48 | { 49 | public final int dimension; 50 | public final int chunkX; 51 | public final int chunkZ; 52 | 53 | public DimChunkCoord(int dim, ChunkCoordIntPair coord) { 54 | this(dim, coord.chunkXPos, coord.chunkZPos); 55 | } 56 | 57 | public DimChunkCoord(int dim, int x, int z) { 58 | dimension = dim; 59 | chunkX = x; 60 | chunkZ = z; 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return ((chunkX * 31) + chunkZ) * 31 + dimension; 66 | } 67 | 68 | @Override 69 | public boolean equals(Object o) { 70 | if (o instanceof DimChunkCoord) { 71 | DimChunkCoord o2 = (DimChunkCoord) o; 72 | return dimension == o2.dimension && chunkX == o2.chunkX && chunkZ == o2.chunkZ; 73 | } 74 | return false; 75 | } 76 | 77 | public ChunkCoordIntPair getChunkCoord() { 78 | return new ChunkCoordIntPair(chunkX, chunkZ); 79 | } 80 | } 81 | 82 | private static abstract class TicketManager 83 | { 84 | public HashMap> ticketsWithSpace = new HashMap>(); 85 | public HashMap heldChunks = new HashMap(); 86 | 87 | protected void addChunk(DimChunkCoord coord) { 88 | if (heldChunks.containsKey(coord)) 89 | return; 90 | 91 | Stack freeTickets = ticketsWithSpace.get(coord.dimension); 92 | if (freeTickets == null) 93 | ticketsWithSpace.put(coord.dimension, freeTickets = new Stack()); 94 | 95 | Ticket ticket; 96 | if (freeTickets.isEmpty()) 97 | freeTickets.push(ticket = createTicket(coord.dimension)); 98 | else 99 | ticket = freeTickets.peek(); 100 | 101 | ForgeChunkManager.forceChunk(ticket, coord.getChunkCoord()); 102 | heldChunks.put(coord, ticket); 103 | if (ticket.getChunkList().size() == ticket.getChunkListDepth() && !freeTickets.isEmpty()) 104 | freeTickets.pop(); 105 | } 106 | 107 | protected abstract Ticket createTicket(int dimension); 108 | 109 | protected void remChunk(DimChunkCoord coord) { 110 | Ticket ticket = heldChunks.remove(coord); 111 | if (ticket == null) 112 | return; 113 | 114 | ForgeChunkManager.unforceChunk(ticket, coord.getChunkCoord()); 115 | 116 | if (ticket.getChunkList().size() == ticket.getChunkListDepth() - 1) { 117 | Stack freeTickets = ticketsWithSpace.get(coord.dimension); 118 | if (freeTickets == null) 119 | ticketsWithSpace.put(coord.dimension, freeTickets = new Stack()); 120 | freeTickets.push(ticket); 121 | } 122 | } 123 | 124 | protected void unloadDimension(int dimension) { 125 | ticketsWithSpace.remove(dimension); 126 | } 127 | } 128 | 129 | private static abstract class ChunkLoaderOrganiser extends TicketManager 130 | { 131 | private HashMap> dormantLoaders = new HashMap>(); 132 | private HashMap> forcedChunksByChunk = new HashMap>(); 133 | private HashMap> forcedChunksByLoader = new HashMap>(); 134 | private HashMap timedUnloadQueue = new HashMap(); 135 | 136 | private boolean reviving; 137 | private boolean dormant = false; 138 | 139 | public boolean canForceNewChunks(int dimension, Collection chunks) { 140 | if (dormant) 141 | return true; 142 | 143 | int required = 0; 144 | for (ChunkCoordIntPair coord : chunks) { 145 | LinkedList loaders = forcedChunksByChunk.get(new DimChunkCoord(dimension, coord)); 146 | if (loaders == null || loaders.isEmpty()) 147 | required++; 148 | } 149 | return canForceNewChunks(required, dimension); 150 | } 151 | 152 | public final int numLoadedChunks() { 153 | return forcedChunksByChunk.size(); 154 | } 155 | 156 | public void addChunkLoader(IChickenChunkLoader loader) { 157 | if (reviving) 158 | return; 159 | 160 | int dim = CommonUtils.getDimension(loader.getWorld()); 161 | if (dormant) { 162 | HashSet coords = dormantLoaders.get(dim); 163 | if (coords == null) 164 | dormantLoaders.put(dim, coords = new HashSet()); 165 | coords.add(loader.getPosition()); 166 | } else { 167 | forcedChunksByLoader.put(loader, new HashSet()); 168 | forceChunks(loader, dim, loader.getChunks()); 169 | } 170 | setDirty(); 171 | } 172 | 173 | public void remChunkLoader(IChickenChunkLoader loader) { 174 | int dim = CommonUtils.getDimension(loader.getWorld()); 175 | if (dormant) { 176 | HashSet coords = dormantLoaders.get(dim); 177 | if(coords != null) 178 | coords.remove(loader.getPosition()); 179 | } else { 180 | HashSet chunks = forcedChunksByLoader.remove(loader); 181 | if (chunks == null) 182 | return; 183 | unforceChunks(loader, dim, chunks, true); 184 | } 185 | setDirty(); 186 | } 187 | 188 | private void unforceChunks(IChickenChunkLoader loader, int dim, Collection chunks, boolean remLoader) { 189 | for (ChunkCoordIntPair coord : chunks) { 190 | DimChunkCoord dimCoord = new DimChunkCoord(dim, coord); 191 | LinkedList loaders = forcedChunksByChunk.get(dimCoord); 192 | if (loaders == null || !loaders.remove(loader)) 193 | continue; 194 | 195 | if (loaders.isEmpty()) { 196 | forcedChunksByChunk.remove(dimCoord); 197 | timedUnloadQueue.put(dimCoord, 100); 198 | } 199 | } 200 | 201 | if (!remLoader) 202 | forcedChunksByLoader.get(loader).removeAll(chunks); 203 | setDirty(); 204 | } 205 | 206 | private void forceChunks(IChickenChunkLoader loader, int dim, Collection chunks) { 207 | for (ChunkCoordIntPair coord : chunks) { 208 | DimChunkCoord dimCoord = new DimChunkCoord(dim, coord); 209 | LinkedList loaders = forcedChunksByChunk.get(dimCoord); 210 | if (loaders == null) 211 | forcedChunksByChunk.put(dimCoord, loaders = new LinkedList()); 212 | if (loaders.isEmpty()) { 213 | timedUnloadQueue.remove(dimCoord); 214 | addChunk(dimCoord); 215 | } 216 | 217 | if (!loaders.contains(loader)) 218 | loaders.add(loader); 219 | } 220 | 221 | forcedChunksByLoader.get(loader).addAll(chunks); 222 | setDirty(); 223 | } 224 | 225 | public abstract boolean canForceNewChunks(int newChunks, int dim); 226 | 227 | public abstract void setDirty(); 228 | 229 | public void updateChunkLoader(IChickenChunkLoader loader) { 230 | HashSet loaderChunks = forcedChunksByLoader.get(loader); 231 | if (loaderChunks == null) { 232 | addChunkLoader(loader); 233 | return; 234 | } 235 | HashSet oldChunks = new HashSet(loaderChunks); 236 | HashSet newChunks = new HashSet(); 237 | for (ChunkCoordIntPair chunk : loader.getChunks()) 238 | if (!oldChunks.remove(chunk)) 239 | newChunks.add(chunk); 240 | 241 | int dim = CommonUtils.getDimension(loader.getWorld()); 242 | if (!oldChunks.isEmpty()) 243 | unforceChunks(loader, dim, oldChunks, false); 244 | if (!newChunks.isEmpty()) 245 | forceChunks(loader, dim, newChunks); 246 | } 247 | 248 | public void save(DataOutput dataout) throws IOException { 249 | dataout.writeInt(dormantLoaders.size()); 250 | for (Entry> entry : dormantLoaders.entrySet()) { 251 | dataout.writeInt(entry.getKey()); 252 | HashSet coords = entry.getValue(); 253 | dataout.writeInt(coords.size()); 254 | for (BlockCoord coord : coords) { 255 | dataout.writeInt(coord.x); 256 | dataout.writeInt(coord.y); 257 | dataout.writeInt(coord.z); 258 | } 259 | } 260 | dataout.writeInt(forcedChunksByLoader.size()); 261 | for (IChickenChunkLoader loader : forcedChunksByLoader.keySet()) { 262 | BlockCoord coord = loader.getPosition(); 263 | dataout.writeInt(CommonUtils.getDimension(loader.getWorld())); 264 | dataout.writeInt(coord.x); 265 | dataout.writeInt(coord.y); 266 | dataout.writeInt(coord.z); 267 | } 268 | } 269 | 270 | public void load(DataInputStream datain) throws IOException { 271 | int dimensions = datain.readInt(); 272 | for (int i = 0; i < dimensions; i++) { 273 | int dim = datain.readInt(); 274 | HashSet coords = new HashSet(); 275 | dormantLoaders.put(dim, coords); 276 | int numCoords = datain.readInt(); 277 | for (int j = 0; j < numCoords; j++) { 278 | coords.add(new BlockCoord(datain.readInt(), datain.readInt(), datain.readInt())); 279 | } 280 | } 281 | int numLoaders = datain.readInt(); 282 | for (int i = 0; i < numLoaders; i++) { 283 | int dim = datain.readInt(); 284 | HashSet coords = dormantLoaders.get(dim); 285 | if (coords == null) 286 | dormantLoaders.put(dim, coords = new HashSet()); 287 | coords.add(new BlockCoord(datain.readInt(), datain.readInt(), datain.readInt())); 288 | } 289 | } 290 | 291 | public void revive() { 292 | if (!dormant) 293 | return; 294 | dormant = false; 295 | for (int dim : dormantLoaders.keySet()) { 296 | World world = getWorld(dim, reloadDimensions); 297 | if (world != null) 298 | revive(world); 299 | } 300 | } 301 | 302 | public void devive() { 303 | if (dormant) 304 | return; 305 | 306 | for (IChickenChunkLoader loader : new ArrayList(forcedChunksByLoader.keySet())) { 307 | int dim = CommonUtils.getDimension(loader.getWorld()); 308 | HashSet coords = dormantLoaders.get(dim); 309 | if (coords == null) 310 | dormantLoaders.put(dim, coords = new HashSet()); 311 | coords.add(loader.getPosition()); 312 | remChunkLoader(loader); 313 | } 314 | 315 | dormant = true; 316 | } 317 | 318 | public void revive(World world) { 319 | HashSet coords = dormantLoaders.get(CommonUtils.getDimension(world)); 320 | if (coords == null) 321 | return; 322 | 323 | //addChunkLoader will add to the coord set if we are dormant 324 | ArrayList verifyCoords = new ArrayList(coords); 325 | coords.clear(); 326 | 327 | for (BlockCoord coord : verifyCoords) { 328 | reviving = true; 329 | TileEntity tile = world.getTileEntity(coord.x, coord.y, coord.z); 330 | reviving = false; 331 | if (tile instanceof IChickenChunkLoader) { 332 | ChunkLoaderManager.addChunkLoader((IChickenChunkLoader) tile); 333 | } 334 | } 335 | } 336 | 337 | public void setDormant() { 338 | dormant = true; 339 | } 340 | 341 | public boolean isDormant() { 342 | return dormant; 343 | } 344 | 345 | public void tickDownUnloads() { 346 | for (Iterator> iterator = timedUnloadQueue.entrySet().iterator(); iterator.hasNext(); ) { 347 | Entry entry = iterator.next(); 348 | int ticks = entry.getValue(); 349 | if (ticks <= 1) { 350 | remChunk(entry.getKey()); 351 | iterator.remove(); 352 | } else { 353 | entry.setValue(ticks - 1); 354 | } 355 | } 356 | } 357 | } 358 | 359 | private static class PlayerOrganiser extends ChunkLoaderOrganiser 360 | { 361 | private static boolean dirty; 362 | 363 | public final String username; 364 | 365 | public PlayerOrganiser(String username) { 366 | this.username = username; 367 | } 368 | 369 | public boolean canForceNewChunks(int required, int dim) { 370 | return required + numLoadedChunks() < getPlayerChunkLimit(username) && required < ForgeChunkManager.ticketCountAvailableFor(username) * ForgeChunkManager.getMaxChunkDepthFor("ChickenChunks"); 371 | } 372 | 373 | @Override 374 | public Ticket createTicket(int dimension) { 375 | return ForgeChunkManager.requestPlayerTicket(ChickenChunks.instance, username, DimensionManager.getWorld(dimension), Type.NORMAL); 376 | } 377 | 378 | @Override 379 | public void setDirty() { 380 | dirty = true; 381 | } 382 | } 383 | 384 | private static class ModOrganiser extends ChunkLoaderOrganiser 385 | { 386 | public final Object mod; 387 | public final ModContainer container; 388 | private boolean dirty; 389 | 390 | public ModOrganiser(Object mod, ModContainer container) { 391 | this.mod = mod; 392 | this.container = container; 393 | } 394 | 395 | @Override 396 | public boolean canForceNewChunks(int required, int dim) { 397 | return required < ForgeChunkManager.ticketCountAvailableFor(mod, DimensionManager.getWorld(dim)) * ForgeChunkManager.getMaxChunkDepthFor(container.getModId()); 398 | } 399 | 400 | @Override 401 | public void setDirty() { 402 | dirty = false; 403 | } 404 | 405 | @Override 406 | protected Ticket createTicket(int dimension) { 407 | return ForgeChunkManager.requestTicket(mod, DimensionManager.getWorld(dimension), Type.NORMAL); 408 | } 409 | } 410 | 411 | private static class DummyLoadingCallback implements OrderedLoadingCallback, PlayerOrderedLoadingCallback 412 | { 413 | @Override 414 | public void ticketsLoaded(List tickets, World world) { 415 | } 416 | 417 | @Override 418 | public List ticketsLoaded(List tickets, World world, int maxTicketCount) { 419 | return new LinkedList(); 420 | } 421 | 422 | @Override 423 | public ListMultimap playerTicketsLoaded(ListMultimap tickets, World world) { 424 | return LinkedListMultimap.create(); 425 | } 426 | } 427 | 428 | private static enum ReviveChange 429 | { 430 | PlayerRevive, 431 | PlayerDevive, 432 | ModRevive, 433 | DimensionRevive; 434 | 435 | public LinkedList list; 436 | 437 | public static void load() { 438 | for (ReviveChange change : values()) 439 | change.list = new LinkedList(); 440 | } 441 | } 442 | 443 | private static boolean reloadDimensions = false; 444 | private static boolean opInteract = false; 445 | private static int cleanupTicks; 446 | private static int maxChunks; 447 | private static int awayTimeout; 448 | private static HashMap mods = new HashMap(); 449 | 450 | private static boolean loaded = false; 451 | private static HashMap playerOrganisers; 452 | private static HashMap modOrganisers; 453 | private static HashMap loginTimes; 454 | private static File saveDir; 455 | 456 | /** 457 | * By doing this you are delegating all chunks from your mod to be handled by yours truly. 458 | */ 459 | public static void registerMod(Object mod) { 460 | ModContainer container = Loader.instance().getModObjectList().inverse().get(mod); 461 | if (container == null) 462 | throw new NullPointerException("Mod container not found for: " + mod); 463 | mods.put(mod, container); 464 | ForgeChunkManager.setForcedChunkLoadingCallback(mod, new DummyLoadingCallback()); 465 | } 466 | 467 | public static void loadWorld(WorldServer world) { 468 | ReviveChange.DimensionRevive.list.add(world); 469 | } 470 | 471 | public static World getWorld(int dim, boolean create) { 472 | if (create) 473 | return MinecraftServer.getServer().worldServerForDimension(dim); 474 | return DimensionManager.getWorld(dim); 475 | } 476 | 477 | public static void load(WorldServer world) { 478 | if (loaded) 479 | return; 480 | 481 | loaded = true; 482 | 483 | playerOrganisers = new HashMap(); 484 | modOrganisers = new HashMap(); 485 | loginTimes = new HashMap(); 486 | ReviveChange.load(); 487 | 488 | try { 489 | saveDir = new File(DimensionManager.getCurrentSaveRootDirectory(), "chickenchunks"); 490 | if (!saveDir.exists()) 491 | saveDir.mkdirs(); 492 | loadPlayerChunks(); 493 | loadModChunks(); 494 | loadLoginTimes(); 495 | } catch (Exception e) { 496 | throw new RuntimeException(e); 497 | } 498 | } 499 | 500 | private static void loadLoginTimes() throws IOException { 501 | File saveFile = new File(saveDir, "loginTimes.dat"); 502 | if (!saveFile.exists()) 503 | return; 504 | 505 | DataInputStream datain = new DataInputStream(new FileInputStream(saveFile)); 506 | try { 507 | int entries = datain.readInt(); 508 | for (int i = 0; i < entries; i++) 509 | loginTimes.put(datain.readUTF(), datain.readLong()); 510 | } catch (IOException e) { 511 | LogManager.getLogger("ChickenChunks").error("Error reading loginTimes.dat", e); 512 | } 513 | datain.close(); 514 | 515 | } 516 | 517 | private static void loadModChunks() throws IOException { 518 | for (Entry entry : mods.entrySet()) { 519 | File saveFile = new File(saveDir, entry.getValue().getModId() + ".dat"); 520 | if (!saveFile.exists()) 521 | return; 522 | 523 | DataInputStream datain = new DataInputStream(new FileInputStream(saveFile)); 524 | ModOrganiser organiser = getModOrganiser(entry.getKey()); 525 | ReviveChange.ModRevive.list.add(organiser); 526 | 527 | organiser.load(datain); 528 | datain.close(); 529 | } 530 | } 531 | 532 | private static void loadPlayerChunks() throws IOException { 533 | File saveFile = new File(saveDir, "players.dat"); 534 | if (!saveFile.exists()) 535 | return; 536 | 537 | DataInputStream datain = new DataInputStream(new FileInputStream(saveFile)); 538 | int organisers = datain.readInt(); 539 | for (int i = 0; i < organisers; i++) { 540 | String username = datain.readUTF(); 541 | PlayerOrganiser organiser = getPlayerOrganiser(username); 542 | organiser.setDormant(); 543 | if (allowOffline(username) && loggedInRecently(username)) 544 | ReviveChange.PlayerRevive.list.add(organiser); 545 | 546 | organiser.load(datain); 547 | } 548 | datain.close(); 549 | } 550 | 551 | private static boolean loggedInRecently(String username) { 552 | if (awayTimeout == 0) 553 | return true; 554 | 555 | Long lastLogin = loginTimes.get(username); 556 | return lastLogin != null && (System.currentTimeMillis() - lastLogin) / 60000L < awayTimeout; 557 | 558 | } 559 | 560 | public static int getPlayerChunkLimit(String username) { 561 | ConfigTag config = ChickenChunks.config.getTag("players"); 562 | if (config.containsTag(username)) { 563 | int ret = config.getTag(username).getIntValue(0); 564 | if (ret != 0) 565 | return ret; 566 | } 567 | 568 | if (ServerUtils.isPlayerOP(username)) { 569 | int ret = config.getTag("OP").getIntValue(0); 570 | if (ret != 0) 571 | return ret; 572 | } 573 | 574 | return config.getTag("DEFAULT").getIntValue(5000); 575 | } 576 | 577 | public static boolean allowOffline(String username) { 578 | ConfigTag config = ChickenChunks.config.getTag("allowoffline"); 579 | if (config.containsTag(username)) 580 | return config.getTag(username).getBooleanValue(true); 581 | 582 | if (ServerUtils.isPlayerOP(username)) 583 | return config.getTag("OP").getBooleanValue(true); 584 | 585 | return config.getTag("DEFAULT").getBooleanValue(true); 586 | } 587 | 588 | public static boolean allowChunkViewer(String username) { 589 | ConfigTag config = ChickenChunks.config.getTag("allowchunkviewer"); 590 | if (config.containsTag(username)) 591 | return config.getTag(username).getBooleanValue(true); 592 | 593 | if (ServerUtils.isPlayerOP(username)) 594 | return config.getTag("OP").getBooleanValue(true); 595 | 596 | return config.getTag("DEFAULT").getBooleanValue(true); 597 | } 598 | 599 | public static void initConfig(ConfigFile config) { 600 | config.getTag("players").setPosition(0).useBraces().setComment("Per player chunk limiting. Values ignored if 0.:Simply add ="); 601 | config.getTag("players.DEFAULT").setComment("Forge gives everyone 12500 by default").getIntValue(5000); 602 | config.getTag("players.OP").setComment("For server op's only.").getIntValue(5000); 603 | config.getTag("allowoffline").setPosition(1).useBraces().setComment("If set to false, players will have to be logged in for their chunkloaders to work.:Simply add ="); 604 | config.getTag("allowoffline.DEFAULT").getBooleanValue(true); 605 | config.getTag("allowoffline.OP").getBooleanValue(true); 606 | config.getTag("allowchunkviewer").setPosition(2).useBraces().setComment("Set to false to deny a player access to the chunk viewer"); 607 | config.getTag("allowchunkviewer.DEFAULT").getBooleanValue(true); 608 | config.getTag("allowchunkviewer.OP").getBooleanValue(true); 609 | if (!FMLCommonHandler.instance().getModName().contains("mcpc")) 610 | cleanupTicks = config.getTag("cleanuptime") 611 | .setComment("The number of ticks to wait between attempting to unload orphaned chunks") 612 | .getIntValue(1200); 613 | reloadDimensions = config.getTag("reload-dimensions") 614 | .setComment("Set to false to disable the automatic reloading of mystcraft dimensions on server restart") 615 | .getBooleanValue(true); 616 | opInteract = config.getTag("op-interact") 617 | .setComment("Enabling this lets OPs alter other player's chunkloaders. WARNING: If you change a chunkloader, you have no idea what may break/explode by not being chunkloaded.") 618 | .getBooleanValue(false); 619 | maxChunks = config.getTag("maxchunks") 620 | .setComment("The maximum number of chunks per chunkloader") 621 | .getIntValue(400); 622 | awayTimeout = config.getTag("awayTimeout") 623 | .setComment("The number of minutes since last login within which chunks from a player will remain active, 0 for infinite.") 624 | .getIntValue(0); 625 | } 626 | 627 | public static void addChunkLoader(IChickenChunkLoader loader) { 628 | int dim = CommonUtils.getDimension(loader.getWorld()); 629 | ChunkLoaderOrganiser organiser = getOrganiser(loader); 630 | if (organiser.canForceNewChunks(dim, loader.getChunks())) 631 | organiser.addChunkLoader(loader); 632 | else 633 | loader.deactivate(); 634 | } 635 | 636 | private static ChunkLoaderOrganiser getOrganiser(IChickenChunkLoader loader) { 637 | String owner = loader.getOwner(); 638 | return owner == null ? getModOrganiser(loader.getMod()) : getPlayerOrganiser(owner); 639 | } 640 | 641 | public static void remChunkLoader(IChickenChunkLoader loader) { 642 | getOrganiser(loader).remChunkLoader(loader); 643 | } 644 | 645 | public static void updateLoader(IChickenChunkLoader loader) { 646 | getOrganiser(loader).updateChunkLoader(loader); 647 | } 648 | 649 | public static boolean canLoaderAdd(IChickenChunkLoader loader, Collection chunks) { 650 | String owner = loader.getOwner(); 651 | int dim = CommonUtils.getDimension(loader.getWorld()); 652 | if (owner != null) 653 | return getPlayerOrganiser(owner).canForceNewChunks(dim, chunks); 654 | 655 | return false; 656 | } 657 | 658 | private static PlayerOrganiser getPlayerOrganiser(String username) { 659 | PlayerOrganiser organiser = playerOrganisers.get(username); 660 | if (organiser == null) 661 | playerOrganisers.put(username, organiser = new PlayerOrganiser(username)); 662 | return organiser; 663 | } 664 | 665 | private static ModOrganiser getModOrganiser(Object mod) { 666 | ModOrganiser organiser = modOrganisers.get(mod); 667 | if (organiser == null) { 668 | ModContainer container = mods.get(mod); 669 | if (container == null) 670 | throw new NullPointerException("Mod not registered with chickenchunks: " + mod); 671 | modOrganisers.put(mod, organiser = new ModOrganiser(mod, container)); 672 | } 673 | return organiser; 674 | } 675 | 676 | public static void serverShutdown() { 677 | loaded = false; 678 | } 679 | 680 | public static void save(WorldServer world) { 681 | try { 682 | if (PlayerOrganiser.dirty) { 683 | File saveFile = new File(saveDir, "players.dat"); 684 | if (!saveFile.exists()) 685 | saveFile.createNewFile(); 686 | DataOutputStream dataout = new DataOutputStream(new FileOutputStream(saveFile)); 687 | dataout.writeInt(playerOrganisers.size()); 688 | for (PlayerOrganiser organiser : playerOrganisers.values()) { 689 | dataout.writeUTF(organiser.username); 690 | organiser.save(dataout); 691 | } 692 | dataout.close(); 693 | PlayerOrganiser.dirty = false; 694 | } 695 | 696 | 697 | for (ModOrganiser organiser : modOrganisers.values()) { 698 | if (organiser.dirty) { 699 | File saveFile = new File(saveDir, organiser.container.getModId() + ".dat"); 700 | if (!saveFile.exists()) 701 | saveFile.createNewFile(); 702 | 703 | DataOutputStream dataout = new DataOutputStream(new FileOutputStream(saveFile)); 704 | organiser.save(dataout); 705 | dataout.close(); 706 | organiser.dirty = false; 707 | } 708 | } 709 | } catch (Exception e) { 710 | throw new RuntimeException(e); 711 | } 712 | } 713 | 714 | @SuppressWarnings("unchecked") 715 | public static void cleanChunks(WorldServer world) { 716 | int dim = CommonUtils.getDimension(world); 717 | int viewdist = ServerUtils.mc().getConfigurationManager().getViewDistance(); 718 | 719 | HashSet loadedChunks = new HashSet(); 720 | for (EntityPlayer player : ServerUtils.getPlayersInDimension(dim)) { 721 | int playerChunkX = (int) player.posX >> 4; 722 | int playerChunkZ = (int) player.posZ >> 4; 723 | 724 | for (int cx = playerChunkX - viewdist; cx <= playerChunkX + viewdist; cx++) 725 | for (int cz = playerChunkZ - viewdist; cz <= playerChunkZ + viewdist; cz++) 726 | loadedChunks.add(new ChunkCoordIntPair(cx, cz)); 727 | } 728 | 729 | ImmutableSetMultimap persistantChunks = world.getPersistentChunks(); 730 | PlayerManager manager = world.getPlayerManager(); 731 | 732 | for (Chunk chunk : (List) world.theChunkProviderServer.loadedChunks) { 733 | ChunkCoordIntPair coord = chunk.getChunkCoordIntPair(); 734 | if (!loadedChunks.contains(coord) && !persistantChunks.containsKey(coord) && world.theChunkProviderServer.chunkExists(coord.chunkXPos, coord.chunkZPos)) { 735 | PlayerInstance instance = manager.getOrCreateChunkWatcher(coord.chunkXPos, coord.chunkZPos, false); 736 | if (instance == null) { 737 | world.theChunkProviderServer.unloadChunksIfNotNearSpawn(coord.chunkXPos, coord.chunkZPos); 738 | } else { 739 | while (instance.playersWatchingChunk.size() > 0) 740 | instance.removePlayer((EntityPlayerMP) instance.playersWatchingChunk.get(0)); 741 | } 742 | } 743 | } 744 | 745 | if (ServerUtils.getPlayersInDimension(dim).isEmpty() && world.getPersistentChunks().isEmpty() && !DimensionManager.shouldLoadSpawn(dim)) { 746 | DimensionManager.unloadWorld(dim); 747 | } 748 | } 749 | 750 | public static void tickEnd(WorldServer world) { 751 | if (world.getWorldTime() % 1200 == 0) 752 | updateLoginTimes(); 753 | 754 | if (cleanupTicks > 0 && world.getWorldTime() % cleanupTicks == 0) 755 | cleanChunks(world); 756 | 757 | tickDownUnloads(); 758 | revivePlayerLoaders(); 759 | } 760 | 761 | private static void updateLoginTimes() { 762 | long time = System.currentTimeMillis(); 763 | for (EntityPlayer player : ServerUtils.getPlayers()) 764 | loginTimes.put(player.getCommandSenderName(), time); 765 | 766 | try { 767 | File saveFile = new File(saveDir, "loginTimes.dat"); 768 | if (!saveFile.exists()) 769 | saveFile.createNewFile(); 770 | 771 | DataOutputStream dataout = new DataOutputStream(new FileOutputStream(saveFile)); 772 | dataout.writeInt(loginTimes.size()); 773 | for (Entry entry : loginTimes.entrySet()) { 774 | dataout.writeUTF(entry.getKey()); 775 | dataout.writeLong(entry.getValue()); 776 | } 777 | dataout.close(); 778 | } catch (Exception e) { 779 | throw new RuntimeException(e); 780 | } 781 | 782 | for (PlayerOrganiser organiser : playerOrganisers.values()) 783 | if (!organiser.isDormant() && !loggedInRecently(organiser.username)) 784 | ReviveChange.PlayerDevive.list.add(organiser); 785 | } 786 | 787 | private static void tickDownUnloads() { 788 | for (Entry entry : playerOrganisers.entrySet()) 789 | entry.getValue().tickDownUnloads(); 790 | 791 | for (Entry entry : modOrganisers.entrySet()) 792 | entry.getValue().tickDownUnloads(); 793 | } 794 | 795 | private static void revivePlayerLoaders() { 796 | for (Object organiser : ReviveChange.PlayerRevive.list) 797 | ((PlayerOrganiser) organiser).revive(); 798 | ReviveChange.PlayerRevive.list.clear(); 799 | 800 | for (Object organiser : ReviveChange.ModRevive.list) 801 | ((ModOrganiser) organiser).revive(); 802 | ReviveChange.ModRevive.list.clear(); 803 | 804 | for (Object world : ReviveChange.DimensionRevive.list) 805 | for (PlayerOrganiser organiser : playerOrganisers.values()) 806 | organiser.revive((World) world); 807 | ReviveChange.DimensionRevive.list.clear(); 808 | 809 | for (Object organiser : ReviveChange.PlayerDevive.list) 810 | ((PlayerOrganiser) organiser).devive(); 811 | ReviveChange.PlayerDevive.list.clear(); 812 | } 813 | 814 | public static void playerLogin(String username) { 815 | loginTimes.put(username, System.currentTimeMillis()); 816 | ReviveChange.PlayerRevive.list.add(getPlayerOrganiser(username)); 817 | } 818 | 819 | public static void playerLogout(String username) { 820 | if (!allowOffline(username)) 821 | ReviveChange.PlayerDevive.list.add(getPlayerOrganiser(username)); 822 | } 823 | 824 | public static int maxChunksPerLoader() { 825 | return maxChunks; 826 | } 827 | 828 | public static boolean opInteract() { 829 | return opInteract; 830 | } 831 | 832 | public static void unloadWorld(World world) { 833 | int dim = CommonUtils.getDimension(world); 834 | for (TicketManager mgr : playerOrganisers.values()) 835 | mgr.unloadDimension(dim); 836 | for (TicketManager mgr : modOrganisers.values()) 837 | mgr.unloadDimension(dim); 838 | } 839 | } 840 | --------------------------------------------------------------------------------