From dbd27f55ea522876c17a0f63de8173725031cdd9 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 15 Aug 2020 21:08:40 -0500 Subject: [PATCH] Start Forge 1.16.2 work --- forge-1.16.2/.gitignore | 1 + .../bin/main/META-INF/accesstransformer.cfg | 3 + forge-1.16.2/bin/main/META-INF/mods.toml | 25 + forge-1.16.2/bin/main/configuration.txt | 457 ++++ .../ChunkSnapshot$EmptySection.class | Bin 0 -> 2420 bytes .../forge_1_16_1/ChunkSnapshot$Section.class | Bin 0 -> 1869 bytes .../ChunkSnapshot$StdSection.class | Bin 0 -> 2493 bytes .../dynmap/forge_1_16_1/ChunkSnapshot.class | Bin 0 -> 3600 bytes .../org/dynmap/forge_1_16_1/ClientProxy.class | Bin 0 -> 323 bytes .../org/dynmap/forge_1_16_1/DmapCommand.class | Bin 0 -> 2226 bytes .../dynmap/forge_1_16_1/DmarkerCommand.class | Bin 0 -> 2232 bytes .../dynmap/forge_1_16_1/DynmapCommand.class | Bin 0 -> 2230 bytes .../forge_1_16_1/DynmapCommandHandler.class | Bin 0 -> 2973 bytes .../forge_1_16_1/DynmapExpCommand.class | Bin 0 -> 2236 bytes .../forge_1_16_1/DynmapMod$APICallback.class | Bin 0 -> 1439 bytes .../org/dynmap/forge_1_16_1/DynmapMod.class | Bin 0 -> 5591 bytes .../DynmapPlugin$BlockUpdateRec.class | Bin 0 -> 2400 bytes .../DynmapPlugin$ChatHandler.class | Bin 0 -> 2568 bytes .../DynmapPlugin$ChatMessage.class | Bin 0 -> 2470 bytes .../DynmapPlugin$ForgeCommandSender.class | Bin 0 -> 3108 bytes .../DynmapPlugin$ForgePlayer.class | Bin 0 -> 4752 bytes .../DynmapPlugin$ForgeServer.class | Bin 0 -> 8134 bytes .../forge_1_16_1/DynmapPlugin$OurLog.class | Bin 0 -> 3031 bytes .../DynmapPlugin$PlayerTracker.class | Bin 0 -> 3641 bytes .../DynmapPlugin$ProfileTexture.class | Bin 0 -> 2413 bytes .../DynmapPlugin$TaskRecord.class | Bin 0 -> 2659 bytes .../DynmapPlugin$TexturesPayload.class | Bin 0 -> 2721 bytes .../DynmapPlugin$WorldTracker.class | Bin 0 -> 3932 bytes .../dynmap/forge_1_16_1/DynmapPlugin.class | Bin 0 -> 9905 bytes .../ForgeMapChunkCache$EmptyChunk.class | Bin 0 -> 5201 bytes ...ForgeMapChunkCache$OurEndMapIterator.class | Bin 0 -> 4546 bytes .../ForgeMapChunkCache$OurMapIterator.class | Bin 0 -> 10640 bytes .../ForgeMapChunkCache$PlainChunk.class | Bin 0 -> 5278 bytes .../forge_1_16_1/ForgeMapChunkCache.class | Bin 0 -> 8763 bytes .../org/dynmap/forge_1_16_1/ForgeWorld.class | Bin 0 -> 7361 bytes .../main/org/dynmap/forge_1_16_1/Proxy.class | Bin 0 -> 910 bytes .../SnapshotCache$CacheHashMap.class | Bin 0 -> 2176 bytes .../forge_1_16_1/SnapshotCache$CacheRec.class | Bin 0 -> 835 bytes .../SnapshotCache$SnapshotRec.class | Bin 0 -> 533 bytes .../dynmap/forge_1_16_1/SnapshotCache.class | Bin 0 -> 5446 bytes .../dynmap/forge_1_16_1/VersionCheck$1.class | Bin 0 -> 640 bytes .../dynmap/forge_1_16_1/VersionCheck.class | Bin 0 -> 4209 bytes .../permissions/FilePermissions.class | Bin 0 -> 4121 bytes .../permissions/OpPermissions.class | Bin 0 -> 2427 bytes .../permissions/PermissionProvider.class | Bin 0 -> 532 bytes forge-1.16.2/bin/main/pack.mcmeta | 6 + forge-1.16.2/bin/main/permissions.yml.example | 27 + forge-1.16.2/build.gradle | 87 + .../dynmap/forge_1_16_2/ChunkSnapshot.java | 286 +++ .../org/dynmap/forge_1_16_2/ClientProxy.java | 6 + .../org/dynmap/forge_1_16_2/DynmapMod.java | 127 ++ .../org/dynmap/forge_1_16_2/DynmapPlugin.java | 2016 +++++++++++++++++ .../forge_1_16_2/ForgeMapChunkCache.java | 1489 ++++++++++++ .../org/dynmap/forge_1_16_2/ForgeWorld.java | 234 ++ .../java/org/dynmap/forge_1_16_2/Proxy.java | 24 + .../dynmap/forge_1_16_2/SnapshotCache.java | 191 ++ .../org/dynmap/forge_1_16_2/VersionCheck.java | 97 + .../permissions/FilePermissions.java | 103 + .../permissions/OpPermissions.java | 51 + .../permissions/PermissionProvider.java | 15 + .../resources/META-INF/accesstransformer.cfg | 3 + .../src/main/resources/META-INF/mods.toml | 25 + .../src/main/resources/configuration.txt | 457 ++++ forge-1.16.2/src/main/resources/pack.mcmeta | 6 + .../main/resources/permissions.yml.example | 27 + settings.gradle | 2 + 66 files changed, 5765 insertions(+) create mode 100644 forge-1.16.2/.gitignore create mode 100644 forge-1.16.2/bin/main/META-INF/accesstransformer.cfg create mode 100644 forge-1.16.2/bin/main/META-INF/mods.toml create mode 100644 forge-1.16.2/bin/main/configuration.txt create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$EmptySection.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$Section.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$StdSection.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ClientProxy.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmapCommand.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmarkerCommand.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapCommand.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapCommandHandler.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapExpCommand.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod$APICallback.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$BlockUpdateRec.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ChatHandler.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ChatMessage.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ForgeCommandSender.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ForgePlayer.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ForgeServer.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$OurLog.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$PlayerTracker.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ProfileTexture.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TaskRecord.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TexturesPayload.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$WorldTracker.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$EmptyChunk.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$OurEndMapIterator.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$OurMapIterator.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$PlainChunk.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeWorld.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/Proxy.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheHashMap.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheRec.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$SnapshotRec.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/VersionCheck$1.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/VersionCheck.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/FilePermissions.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/OpPermissions.class create mode 100644 forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/PermissionProvider.class create mode 100644 forge-1.16.2/bin/main/pack.mcmeta create mode 100644 forge-1.16.2/bin/main/permissions.yml.example create mode 100644 forge-1.16.2/build.gradle create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ChunkSnapshot.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ClientProxy.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapMod.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapPlugin.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeMapChunkCache.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeWorld.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/Proxy.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/SnapshotCache.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/VersionCheck.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/FilePermissions.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/OpPermissions.java create mode 100644 forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/PermissionProvider.java create mode 100644 forge-1.16.2/src/main/resources/META-INF/accesstransformer.cfg create mode 100644 forge-1.16.2/src/main/resources/META-INF/mods.toml create mode 100644 forge-1.16.2/src/main/resources/configuration.txt create mode 100644 forge-1.16.2/src/main/resources/pack.mcmeta create mode 100644 forge-1.16.2/src/main/resources/permissions.yml.example diff --git a/forge-1.16.2/.gitignore b/forge-1.16.2/.gitignore new file mode 100644 index 00000000..84c048a7 --- /dev/null +++ b/forge-1.16.2/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/forge-1.16.2/bin/main/META-INF/accesstransformer.cfg b/forge-1.16.2/bin/main/META-INF/accesstransformer.cfg new file mode 100644 index 00000000..b9c90dd4 --- /dev/null +++ b/forge-1.16.2/bin/main/META-INF/accesstransformer.cfg @@ -0,0 +1,3 @@ +public net.minecraft.world.server.ChunkManager field_219251_e # loaded chunk list +public net.minecraft.world.server.ChunkManager field_219252_f # loaded chunk list +public net.minecraft.world.biome.BiomeAmbience field_235206_c_ # waterColor \ No newline at end of file diff --git a/forge-1.16.2/bin/main/META-INF/mods.toml b/forge-1.16.2/bin/main/META-INF/mods.toml new file mode 100644 index 00000000..28d21a89 --- /dev/null +++ b/forge-1.16.2/bin/main/META-INF/mods.toml @@ -0,0 +1,25 @@ +modLoader="javafml" +loaderVersion="[32,)" +issueTrackerURL="https://github.com/webbukkit/dynmap/issues" +[[mods]] +modId="dynmap" +version="${version}" +displayName="Dynmap" +authors="mikeprimm" +description=''' +Dynamic, Google-maps style rendered maps for your Minecraft server +''' + +[[dependencies.dynmap]] + modId="forge" + mandatory=true + versionRange="[32,)" + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side="SERVER" +[[dependencies.dynmap]] + modId="minecraft" + mandatory=true + versionRange="[1.16.1]" + ordering="NONE" + side="SERVER" diff --git a/forge-1.16.2/bin/main/configuration.txt b/forge-1.16.2/bin/main/configuration.txt new file mode 100644 index 00000000..c66a91f3 --- /dev/null +++ b/forge-1.16.2/bin/main/configuration.txt @@ -0,0 +1,457 @@ +# All paths in this configuration file are relative to Dynmap's data-folder: minecraft_server/dynmap/ + +# All map templates are defined in the templates directory +# To use the HDMap very-low-res (2 ppb) map templates as world defaults, set value to vlowres +# The definitions of these templates are in normal-vlowres.txt, nether-vlowres.txt, and the_end-vlowres.txt +# To use the HDMap low-res (4 ppb) map templates as world defaults, set value to lowres +# The definitions of these templates are in normal-lowres.txt, nether-lowres.txt, and the_end-lowres.txt +# To use the HDMap hi-res (16 ppb) map templates (these can take a VERY long time for initial fullrender), set value to hires +# The definitions of these templates are in normal-hires.txt, nether-hires.txt, and the_end-hires.txt +# To use the HDMap low-res (4 ppb) map templates, with support for boosting resolution selectively to hi-res (16 ppb), set value to low_boost_hi +# The definitions of these templates are in normal-low_boost_hi.txt, nether-low_boost_hi.txt, and the_end-low_boost_hi.txt +# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to vhi-res (32 ppb), set value to hi_boost_vhi +# The definitions of these templates are in normal-hi_boost_vhi.txt, nether-hi_boost_vhi.txt, and the_end-hi_boost_vhi.txt +# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to xhi-res (64 ppb), set value to hi_boost_xhi +# The definitions of these templates are in normal-hi_boost_xhi.txt, nether-hi_boost_xhi.txt, and the_end-hi_boost_xhi.txt +deftemplatesuffix: hires + +# Map storage scheme: only uncommoent one 'type' value +# filetree: classic and default scheme: tree of files, with all map data under the directory indicated by 'tilespath' setting +# sqlite: single SQLite database file (this can get VERY BIG), located at 'dbfile' setting (default is file dynmap.db in data directory) +# mysql: MySQL database, at hostname:port in database, accessed via userid with password +storage: + # Filetree storage (standard tree of image files for maps) + type: filetree + # SQLite db for map storage (uses dbfile as storage location) + #type: sqlite + #dbfile: dynmap.db + # MySQL DB for map storage (at 'hostname':'port' in database 'database' using user 'userid' password 'password' and table prefix 'prefix' + #type: mysql + #hostname: localhost + #port: 3306 + #database: dynmap + #userid: dynmap + #password: dynmap + #prefix: "" + +components: + - class: org.dynmap.ClientConfigurationComponent + + - class: org.dynmap.InternalClientUpdateComponent + sendhealth: true + sendposition: true + allowwebchat: true + webchat-interval: 5 + hidewebchatip: false + trustclientname: false + includehiddenplayers: false + # (optional) if true, color codes in player display names are used + use-name-colors: false + # (optional) if true, player login IDs will be used for web chat when their IPs match + use-player-login-ip: true + # (optional) if use-player-login-ip is true, setting this to true will cause chat messages not matching a known player IP to be ignored + require-player-login-ip: false + # (optional) block player login IDs that are banned from chatting + block-banned-player-chat: true + # Require login for web-to-server chat (requires login-enabled: true) + webchat-requires-login: false + # If set to true, users must have dynmap.webchat permission in order to chat + webchat-permissions: false + # Limit length of single chat messages + chatlengthlimit: 256 + # # Optional - make players hidden when they are inside/underground/in shadows (#=light level: 0=full shadow,15=sky) + # hideifshadow: 4 + # # Optional - make player hidden when they are under cover (#=sky light level,0=underground,15=open to sky) + # hideifundercover: 14 + # # (Optional) if true, players that are crouching/sneaking will be hidden + hideifsneaking: false + # If true, player positions/status is protected (login with ID with dynmap.playermarkers.seeall permission required for info other than self) + protected-player-info: false + # If true, hide players with invisibility potion effects active + hide-if-invisiblity-potion: true + # If true, player names are not shown on map, chat, list + hidenames: false + #- class: org.dynmap.JsonFileClientUpdateComponent + # writeinterval: 1 + # sendhealth: true + # sendposition: true + # allowwebchat: true + # webchat-interval: 5 + # hidewebchatip: false + # includehiddenplayers: false + # use-name-colors: false + # use-player-login-ip: false + # require-player-login-ip: false + # block-banned-player-chat: true + # hideifshadow: 0 + # hideifundercover: 0 + # hideifsneaking: false + # # Require login for web-to-server chat (requires login-enabled: true) + # webchat-requires-login: false + # # If set to true, users must have dynmap.webchat permission in order to chat + # webchat-permissions: false + # # Limit length of single chat messages + # chatlengthlimit: 256 + # hide-if-invisiblity-potion: true + # hidenames: false + + - class: org.dynmap.SimpleWebChatComponent + allowchat: true + # If true, web UI users can supply name for chat using 'playername' URL parameter. 'trustclientname' must also be set true. + allowurlname: false + + # Note: this component is needed for the dmarker commands, and for the Marker API to be available to other plugins + - class: org.dynmap.MarkersComponent + type: markers + showlabel: false + enablesigns: false + # Default marker set for sign markers + default-sign-set: markers + # (optional) add spawn point markers to standard marker layer + showspawn: true + spawnicon: world + spawnlabel: "Spawn" + # (optional) layer for showing offline player's positions (for 'maxofflinetime' minutes after logoff) + showofflineplayers: false + offlinelabel: "Offline" + offlineicon: offlineuser + offlinehidebydefault: true + offlineminzoom: 0 + maxofflinetime: 30 + # (optional) layer for showing player's spawn beds + showspawnbeds: false + spawnbedlabel: "Spawn Beds" + spawnbedicon: bed + spawnbedhidebydefault: true + spawnbedminzoom: 0 + spawnbedformat: "%name%'s bed" + # (optional) Show world border (vanilla 1.8+) + showworldborder: true + worldborderlabel: "Border" + + - class: org.dynmap.ClientComponent + type: chat + allowurlname: false + - class: org.dynmap.ClientComponent + type: chatballoon + focuschatballoons: false + - class: org.dynmap.ClientComponent + type: chatbox + showplayerfaces: true + messagettl: 5 + # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages + #scrollback: 100 + # Optional: set maximum number of lines visible for chatbox + #visiblelines: 10 + # Optional: send push button + sendbutton: false + - class: org.dynmap.ClientComponent + type: playermarkers + showplayerfaces: true + showplayerhealth: true + # If true, show player body too (only valid if showplayerfaces=true + showplayerbody: false + # Option to make player faces small - don't use with showplayerhealth + smallplayerfaces: false + # Optional - make player faces layer hidden by default + hidebydefault: false + # Optional - ordering priority in layer menu (low goes before high - default is 0) + layerprio: 0 + # Optional - label for player marker layer (default is 'Players') + label: "Players" + + #- class: org.dynmap.ClientComponent + # type: digitalclock + - class: org.dynmap.ClientComponent + type: link + + - class: org.dynmap.ClientComponent + type: timeofdayclock + showdigitalclock: true + #showweather: true + # Mouse pointer world coordinate display + - class: org.dynmap.ClientComponent + type: coord + label: "Location" + hidey: false + show-mcr: false + show-chunk: false + + # Note: more than one logo component can be defined + #- class: org.dynmap.ClientComponent + # type: logo + # text: "Dynmap" + # #logourl: "images/block_surface.png" + # linkurl: "http://forums.bukkit.org/threads/dynmap.489/" + # # Valid positions: top-left, top-right, bottom-left, bottom-right + # position: bottom-right + + #- class: org.dynmap.ClientComponent + # type: inactive + # timeout: 1800 # in seconds (1800 seconds = 30 minutes) + # redirecturl: inactive.html + # #showmessage: 'You were inactive for too long.' + + #- class: org.dynmap.TestComponent + # stuff: "This is some configuration-value" + +# Treat hiddenplayers.txt as a whitelist for players to be shown on the map? (Default false) +display-whitelist: false + +# How often a tile gets rendered (in seconds). +renderinterval: 1 + +# How many tiles on update queue before accelerate render interval +renderacceleratethreshold: 60 + +# How often to render tiles when backlog is above renderacceleratethreshold +renderaccelerateinterval: 0.2 + +# How many update tiles to work on at once (if not defined, default is 1/2 the number of cores) +tiles-rendered-at-once: 2 + +# If true, use normal priority threads for rendering (versus low priority) - this can keep rendering +# from starving on busy Windows boxes (Linux JVMs pretty much ignore thread priority), but may result +# in more competition for CPU resources with other processes +usenormalthreadpriority: true + +# Save and restore pending tile renders - prevents their loss on server shutdown or /reload +saverestorepending: true + +# Save period for pending jobs (in seconds): periodic saving for crash recovery of jobs +save-pending-period: 900 + +# Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds) +zoomoutperiod: 30 + +# Control whether zoom out tiles are validated on startup (can be needed if zoomout processing is interrupted, but can be expensive on large maps) +initial-zoomout-validate: true + +# Default delay on processing of updated tiles, in seconds. This can reduce potentially expensive re-rendering +# of frequently updated tiles (such as due to machines, pistons, quarries or other automation). Values can +# also be set on individual worlds and individual maps. +tileupdatedelay: 30 + +# Tile hashing is used to minimize tile file updates when no changes have occurred - set to false to disable +enabletilehash: true + +# Optional - hide ores: render as normal stone (so that they aren't revealed by maps) +#hideores: true + +# Optional - enabled BetterGrass style rendering of grass and snow block sides +#better-grass: true + +# Optional - enable smooth lighting by default on all maps supporting it (can be set per map as lighting option) +smooth-lighting: true + +# Optional - use world provider lighting table (good for custom worlds with custom lighting curves, like nether) +# false=classic Dynmap lighting curve +use-brightness-table: true + +# Optional - render specific block names using the textures and models of another block name: can be used to hide/disguise specific +# blocks (e.g. make ores look like stone, hide chests) or to provide simple support for rendering unsupported custom blocks +block-alias: +# "minecraft:quartz_ore": "stone" +# "diamond_ore": "coal_ore" + +# Default image format for HDMaps (png, jpg, jpg-q75, jpg-q80, jpg-q85, jpg-q90, jpg-q95, jpg-q100) +# Has no effect on maps with explicit format settings +image-format: jpg-q90 + +# use-generated-textures: if true, use generated textures (same as client); false is static water/lava textures +# correct-water-lighting: if true, use corrected water lighting (same as client); false is legacy water (darker) +# transparent-leaves: if true, leaves are transparent (lighting-wise): false is needed for some Spout versions that break lighting on leaf blocks +use-generated-textures: true +correct-water-lighting: true +transparent-leaves: true + +# ctm-support: if true, Connected Texture Mod (CTM) in texture packs is enabled (default) +ctm-support: true +# custom-colors-support: if true, Custom Colors in texture packs is enabled (default) +custom-colors-support: true + +# Control loading of player faces (if set to false, skins are never fetched) +#fetchskins: false + +# Control updating of player faces, once loaded (if faces are being managed by other apps or manually) +#refreshskins: false + +# Customize URL used for fetching player skins (%player% is macro for name) +skin-url: "http://skins.minecraft.net/MinecraftSkins/%player%.png" + +# Control behavior for new (1.0+) compass orientation (sunrise moved 90 degrees: east is now what used to be south) +# default is 'newrose' (preserve pre-1.0 maps, rotate rose) +# 'newnorth' is used to rotate maps and rose (requires fullrender of any HDMap map - same as 'newrose' for FlatMap or KzedMap) +compass-mode: newnorth + +# Triggers for automatic updates : blockupdate-with-id is debug for breaking down updates by ID:meta +# To disable, set just 'none' and comment/delete the rest +render-triggers: + - blockupdate + #- blockupdate-with-id + #- lightingupdate + - chunkpopulate + - chunkgenerate + #- none + +# Title for the web page - if not specified, defaults to the server's name (unless it is the default of 'Unknown Server') +#webpage-title: "My Awesome Server Map" + +# The path where the tile-files are placed. +tilespath: web/tiles + +# The path where the web-files are located. +webpath: web + +# The path were the /dynmapexp command exports OBJ ZIP files +exportpath: export + +# The network-interface the webserver will bind to (0.0.0.0 for all interfaces, 127.0.0.1 for only local access). +# If not set, uses same setting as server in server.properties (or 0.0.0.0 if not specified) +#webserver-bindaddress: 0.0.0.0 + +# The TCP-port the webserver will listen on. +webserver-port: 8123 + +# Maximum concurrent session on internal web server - limits resources used in Bukkit server +max-sessions: 30 + +# Disables Webserver portion of Dynmap (Advanced users only) +disable-webserver: false + +# Enable/disable having the web server allow symbolic links (true=compatible with existing code, false=more secure (default)) +allow-symlinks: true + +# Enable login support +login-enabled: false +# Require login to access website (requires login-enabled: true) +login-required: false + +# Period between tile renders for fullrender, in seconds (non-zero to pace fullrenders, lessen CPU load) +timesliceinterval: 0.0 + +# Maximum chunk loads per server tick (1/20th of a second) - reducing this below 90 will impact render performance, but also will reduce server thread load +maxchunkspertick: 200 + +# Progress report interval for fullrender/radiusrender, in tiles. Must be 100 or greater +progressloginterval: 100 + +# Parallel fullrender: if defined, number of concurrent threads used for fullrender or radiusrender +# Note: setting this will result in much more intensive CPU use, some additional memory use. Caution should be used when +# setting this to equal or exceed the number of physical cores on the system. +#parallelrendercnt: 4 + +# Interval the browser should poll for updates. +updaterate: 2000 + +# If nonzero, server will pause fullrender/radiusrender processing when 'fullrenderplayerlimit' or more users are logged in +fullrenderplayerlimit: 0 +# If nonzero, server will pause update render processing when 'updateplayerlimit' or more users are logged in +updateplayerlimit: 0 +# Target limit on server thread use - msec per tick +per-tick-time-limit: 50 +# If TPS of server is below this setting, update renders processing is paused +update-min-tps: 18.0 +# If TPS of server is below this setting, full/radius renders processing is paused +fullrender-min-tps: 18.0 +# If TPS of server is below this setting, zoom out processing is paused +zoomout-min-tps: 18.0 + +showplayerfacesinmenu: true + +# Control whether players that are hidden or not on current map are grayed out (true=yes) +grayplayerswhenhidden: true + +# Set sidebaropened: 'true' to pin menu sidebar opened permanently, 'pinned' to default the sidebar to pinned, but allow it to unpin +#sidebaropened: true + +# Customized HTTP response headers - add 'id: value' pairs to all HTTP response headers (internal web server only) +#http-response-headers: +# Access-Control-Allow-Origin: "my-domain.com" +# X-Custom-Header-Of-Mine: "MyHeaderValue" + +# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields +trusted-proxies: + - "127.0.0.1" + - "0:0:0:0:0:0:0:1" + +joinmessage: "%playername% joined" +quitmessage: "%playername% quit" +spammessage: "You may only chat once every %interval% seconds." +# format for messages from web: %playername% substitutes sender ID (typically IP), %message% includes text +webmsgformat: "&color;2[WEB] %playername%: &color;f%message%" + +# Control whether layer control is presented on the UI (default is true) +showlayercontrol: true + +# Enable checking for banned IPs via banned-ips.txt (internal web server only) +check-banned-ips: true + +# Default selection when map page is loaded +defaultzoom: 0 +defaultworld: world +defaultmap: flat +# (optional) Zoom level and map to switch to when following a player, if possible +#followzoom: 3 +#followmap: surface + +# If true, make persistent record of IP addresses used by player logins, to support web IP to player matching +persist-ids-by-ip: true + +# If true, map text to cyrillic +cyrillic-support: false + +# Messages to customize +msg: + maptypes: "Map Types" + players: "Players" + chatrequireslogin: "Chat Requires Login" + chatnotallowed: "You are not permitted to send chat messages" + hiddennamejoin: "Player joined" + hiddennamequit: "Player quit" + +# URL for client configuration (only need to be tailored for proxies or other non-standard configurations) +url: + # configuration URL + #configuration: "up/configuration" + # update URL + #update: "up/world/{world}/{timestamp}" + # sendmessage URL + #sendmessage: "up/sendmessage" + # login URL + #login: "up/login" + # register URL + #register: "up/register" + # tiles base URL + #tiles: "tiles/" + # markers base URL + #markers: "tiles/" + # Snapshot cache size, in chunks +snapshotcachesize: 500 +# Snapshot cache uses soft references (true), else weak references (false) +soft-ref-cache: true + +# Player enter/exit title messages for map markers +# +# Processing period - how often to check player positions vs markers - default is 1000ms (1 second) +#enterexitperiod: 1000 +# Title message fade in time, in ticks (0.05 second intervals) - default is 10 (1/2 second) +#titleFadeIn: 10 +# Title message stay time, in ticks (0.05 second intervals) - default is 70 (3.5 seconds) +#titleStay: 70 +# Title message fade out time, in ticks (0.05 seocnd intervals) - default is 20 (1 second) +#titleFadeOut: 20 +# Enter/exit messages use on screen titles (true - default), if false chat messages are sent instead +#enterexitUseTitle: true +# Set true if new enter messages should supercede pending exit messages (vs being queued in order), default false +#enterReplacesExits: true + +# Set to true to enable verbose startup messages - can help with debugging map configuration problems +# Set to false for a much quieter startup log +verbose: false + +# Enables debugging. +#debuggers: +# - class: org.dynmap.debug.LogDebugger +# Debug: dump blocks missing render data +dump-missing-blocks: false diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$EmptySection.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$EmptySection.class new file mode 100644 index 0000000000000000000000000000000000000000..526a52f6471ef8f2eb0fb78199f49793831ecd8d GIT binary patch literal 2420 zcmd^B&2Q5%6o1~jrVE9RF&N)?utAGhtD=DfLmU`Gs#M_6F$peHnz*gE#16JQsdxS- zB#_|FAH)&ylC)b_MJPk6gv6na{rvp%`||Jg$FDEn0AK^|TQEc5p;Xa9<$&KfzmPjwo+0R;4I1gd?yss(m!M#b!8B8--+y@p^|N% z1^Owq=XZLHnnB1YT2f8?swyTtrkJX}jQxD!OZT8<9K)#V2q6t^GdcpU(uSd2Q!YHN z&@F#}TOB+tT)efTWPt0BecbbC)%6{%X@~nvCpYsTl*;_=-S&J21F4bJiCvtY`k|w^ zgCU*X4N;2z8T9|m-D$->Q$8~(B{hDkv`hC+(c%&F`t-0tX#5KtQwhcCvieSM4fs5tlF*CM|!c&_ObC#N1 zus8jn&D2ixatSOprR(^+!KPnt-QzMgG2ZJW>_6+u%*z86Dt`k`Kvz6FT2rP{twgYYq^M)`M z*Kv5_-t~ci-I9IfvRBv)F!(349;FP!*ARlKt*B=(ZNVymwM=QiljDgXU>!B$GI}@} z7F2(o)KgTP8YrNN_?yG7KL;~dM+IEX$NvE$Ie`5Y?Dvbs&v5QLu4dpoj`?^8J;8Y~ z(Si%GgexpUxP;>fA&0UAVL1h1Jr&^!Tunu|mV!`EMYwSagh#0eH{sTp4aadvv*Ae! i!YoEMiq;C;#_uA|2;6}+e5`Z$y_>h8g7O?n@BIcoK+*01 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$Section.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ChunkSnapshot$Section.class new file mode 100644 index 0000000000000000000000000000000000000000..afb58237478bb0750029f3f5fe0785d9d8222f56 GIT binary patch literal 1869 zcmd^AO>Yx15FNK^Hdz8GrKNmh5J(AvM3oQ+T5%v0DN=w#+DdSVoLwhzyK67D6Dhw8 z2_(4lgE%6_yBpf(6p$hzaar$p-po9Y?dR`5KYs;)r*Pkc6#`F`9yY_Nj9Jng;6Lx} z^md;0cABlxSe|qxOUy{w2VEXmp`-@|0_(?Y!kQ72!{)pGF**p8_JT-AVP6nft<{eR z6k00e9+U}GPtp5YD_w#Mr10ZVYHn0C;UNuFoQR0I^fb|`AMw~cr+)ch#HpPooFc16 z#;~Ee47ujI@hWBfGE%`w*D}j#z@$``_Bov;ZqT-+!cZYYp^;@YMb`kMWq?%&TE%F8 z9HTF!jUZyi(1D1!$tsIDQQH1(ZNIz#L24Cc=@OR*pD-;Lwsd(F+!p;a+5fZUF01zo z?dL+E?2P}>wk#{}+3V)CCFXXdLOSF&m5~EH$2B4CV-$?+Q_n=g&UEOUA~teG%rwQg zGEUQ2B^p;WE1+ZVpmEjMw^ z4%QCV9v`fgx7t0?sR^g6+tMu6j9X(3!=Az-{Y;eA&}VLB#>b~Hv>WHDHq&4BQRAc?h#0C zNtanLLm+qJ*|t(rr6Es}-*<&#T6#y!r48wKx#t9-(5{kokNNruwKF?yMoquVCir3xJ)rs8uyMi93iBkbw-E4C0a3*Ys!Vo6-MawajS`^MFXuimGtrdeGm6sT55QX z*0jkzrX$PT?@DF<*0!CQz(Hyxb;1zOPW{MH+`*KdorR!8{|x$n7VcT)exiC#q!iup zQSVzyuHCo9te}B z&5@`dRVSqQ<92~Oo*j*uz(S!?sT6-F{50Gk@L>X`9*;AIj7O7yI(6)ALw9SPel==8 zsnF$08WssG{@FupB?RV%5ZlU?NcD^#vBV zBf=j;0tuescksdsFGz?N5Z_)BZ=+S@AVsRg!|u$Pne)xL%{k|nf4={bh?Zz8M;S&p zU2nhAK9Y{vt8_5u&Bf;8{pMmt@Al#7`O8BISk56y~g%Kget>lwEcBlZnG z1K+odaz2)XD;eE=S{n-WMm^$KW|Zwh07jXo02^%y*Wrw&EVHNj9$)L*wi3Y#+hA0P zuhAv$za(VWYzf8N4PhPljAmDAJNjn5`FZt=X1%I6jGb*ple#N?WlFVY+I>DkG);_V z^qt!7)^>DMU*Fx{#0IXQaqjfg(W8UkdcrmXV{1LnZQ0!M zA83W~Mwe^qsK+%}za06r>~Y!V9{0+tp^sN=*E*;xQ*q5QrF50n;@UBASu>O-d`-x< z@ZcKz2wNR+OAEJlJl6sJbsu|et!&w*?`s`lb3eLSI6c=>|Ml*|_z(hW%GJzJ#fv+B zXnMkgNiXh(z(p?%`u|Mbi?aPt{v7I16yxV|%SGWPpIeYy9Im=@gnx`9#t`uv7%ve_* zxR!&=IaL#qZ}*)R_Zq>=MHQ&ImTB+BFF9V!s;)qVxHVkLMoqe2tVyma4$v*n?5K)t zsfv!=>-J@Pd!?~#gn1k0_>$2`v1H`w4x@#$6E*TQay@vyVl+Xnasjl(73#}OD-V|bxxl;DLKuo$P>h(g`%dlp|q zbwe~gAI3op!gq$0+|z?1$o(9lO}IB)&y#Eg0FxwhRAqE$IG9)!v|aHmN9*XkhGbUl z6aGT|gvik*oO3#gAmmo`MuP|M(*k-Vv`REdvouO$n9Wiy9`iIF(gk`c94F#&k}iex zRD5naq~~Y`Gz^Tr482TqbU8s^0sRVH#oipf8jr7GPBa0Yd3qiHH$W{xnmoqB4|MIx zto9wf^%JNJ-NZjY54yUy@ejCgif&OcT$`s-0wK`oya?~my9tC4J(S^giVVM|%1}NB z!f&Yv6}p>{A$y|5Vu}pEry{%u3l1zfKuFW#k5q*B&w=n)D#CsGAR)uR7ilv5or>_` z@CYoEf-plL(Sw8xXL^(6QVe{V~%6=;C-@U>v3EC-{@s&mzV(>|hb;D!Z{(PIW| Uehx}pmD$E7<_a_6iP1v!0s>J;*8l(j literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmapCommand.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmapCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..0588afbf98c95816c8ed28a36fdaae402bacc644 GIT binary patch literal 2226 zcmeHJ-D^`Z6hEom+Fq-5)x8Hq*g$S63?G~bx+xBd6gme!mGs`zE}ENMleEykOi<8w z|0wa?k69nuvK0gizU1TNcRq5GoL_$Y{PGO|j$pqA6$THDopj>4PDI)n(-?={VfV?f z+c_o4iAfTn;~G>MJYEURl}qRwf-AHPcArTt{c{F~2fg*0eKnm(eSCPqpn76rtU-f8 z>w}nyjuLv(d1bA!b-2xHJMXkbXVeU19+@PSN_c5BPpug#Ox#l*G|n%P`+16-oQB1| z!qeXo8vUYw#v`G%@qC2*%3R3LJeQ739ZQRmS94k#qeUH&udy`=%|A_PE#_gQgmZi> z6}rNoOj2Y08jn5cJZiK{Jg>5lqD`c8v^{qNEebfMsBO9Bm&!yR2VQtwYNN=@S`hb! zi#(x0V>{t9VWpt!v90lEmw8)b-yruJsQe=t`WuH7H2RA=P}bHZPx;SRdM$=`tO6}+GfalRM{P472e1e2Ty~$@3Ox7)mZ$eu literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmarkerCommand.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DmarkerCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..e6e976de16bf6bef67e7065c186b9b7aa5878095 GIT binary patch literal 2232 zcmeHJ-D?v;5TA`{FFB*J(Z27Kv>*wA(g!Pr+A0=;1X2q=h0Wb0UAwn??rsA4%LD~| z_m2|i?oz9e9&JD{;LFZ_{N`h4cjvd?e|-K50DG_t`m`V#?;4Qci7z@ zb~`7Du$O3$O_B&52T*13>_%*^zkt3WxI)Wd^M%yXA2Qh8>6L5t)pR2D!R|SO>amG2 zfI5Tb2Qd>JCG@28%35P{y3$zn71P( zoa1Au&=vkQNS@pZ5La9sZ4Y^@WSI#8%179 zLEIZI@`Q%<^@Puam4dFvy2hVf=5>vIh1{>8@{eTbZyZus?_bw}MQvU3lz)Gvw^I0T zx1f=$Gt1a3=Z4Zsy;1?Zi|JcoL#V-n25i7X2HQKmYoZ)@EA=EN(q>O;d^=4>XwS0Z z$Dq|Skx=K0Vz`*B`U~k8lrZ!k|9}Gq;lNC7gs+MG7;F^@DG0os1p*ux+@l``kOYcx ulVm`>J({hM48K6_eJlI~jc+unK$F@Gli?px+sOMNY(XoZdrWNwp8Ny@dimu5 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapCommand.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..5731f62c0e9964f24ec755283af99466c149ae17 GIT binary patch literal 2230 zcmeHJ-D?v;5TA`{FFB*J(Z27C*n(UL6d!CUXjLo(38WT$3Y)u0y1KV}?rsA4%LD~| z_m2|i?o#4IjA=kH;LCo@&c|}y zG5T5WoX0|IKLW?@4SR-pv+JBtTS;C`O3Fr7oDs*K$ znP$fN6%o7AdDLi^ME;$J3~ef%qx9SjtO#&IsO>!E=gPz%`d)Zk>Z7d7N+s?M7eztC z`gW>Mg_VLnk8Q=Df6Uv8eS_L>VDgVt=xkY`p$Bq zE4X20r{1Um-o*7ufCGbD^nU>~13}!SIiS@M pNh>smU!eA`6@G%oH`-O8Nn@^)>))fXQLIDQgH|ECPh$lh`~?SJx7 z5LEQtA0?jMO&V#|H7y7hB|0$1%Uf7TYwyadrG^OvoC!XSS{@H z?S<`y2iptQN<6o$e4k0@89pA@1;`Va*kv7Nc}%+2%jPb(4S^BccL+??`zsBjg>-8K z#saVH3Q1tLo*ijDwu?L#g%oCqKy7Xia5#|pEdu#vEVpwYcWe##k+_d>p@P(X}y0BbT9OjPx;rcbSQ`axJeo5y|buf#TqU|@i-iR*0sL(5$d0XAfusfMIJ0c`d{X>P0X!QjJ zmiwaSZdB4VfxB~cTlto+b|Z1o)WT(s;M(f?tcWmR#@^w&7V#=Ige#e8Yh?6lSkJ|} z#)`#HFLfRw{=f^=mNsqj7(=^9)|j2q|~F{1}q b`An=2saQAR7Gm`FmCfc$D#Gn}M|XY#k<_>? literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapExpCommand.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapExpCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..280ba59a33949b8eee6fe988436509ca6f73d3cb GIT binary patch literal 2236 zcmeHJ&5IK;6o0ARop!9&t?oU?uE-39#e*w?t}8AKQs}zisiZS$H=0StBrWzY6BP9B zA0@u|SnHvVy9L35m%QZV_db)nmv7%ceF1<&*!Q48ps&p=OqMETSvbWx9}mWZhvPx` zyx^UD$c}ZIGL?AHB=DdfT|r+WTyjHT_pwmIJt1(=kLo3day}F4=-`|{^H?X`gEoQg zTee`KWNH?k7^6)KZW3?jwKCjlx!?(nb(#suoY0D9Mo%P9?IY^9&n`H1%ZyVr8kE5W zWjwiuT0I?}(wHfw9i4D`sV<G@Owx&j>xPCv!y@Up_WY*HD zkldE`M4D;iYAi;=IUW2cz=um`<@cNfP7-1`Ax*ZYY8 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod$APICallback.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod$APICallback.class new file mode 100644 index 0000000000000000000000000000000000000000..822a2ce1d237af72b1e2fc88d0156e4e737dff96 GIT binary patch literal 1439 zcmcgsT~8B16g^WaTb2rlAbvY2LV2JKiSa><38@%KijYuAd?M4`fe!A@Y-YD4{0IMl zzr;inefLKh?{2MZHXG`b51rka-h1x3_ng_EzrKD4@C<7XDhwOOp0@l89g3)RLhtf; z^LX?5@n-9FcD85ymFEa&E~L)=8$*WgrU*IoO?oRlklG8Bfx*m?5H@ATG~>CLX|6Gd1AQW$wefGbwio- zjn74-MoHVgFa5?kHz!#b|2z3+Zt^@F#MI}xv{w0&8ctiMNl2X#d_0JxO>6KWygOAO zSkgqESRs1EtR%~IQN#5*u3?5@vC+8{?=Z1SpJw9Dc9fRy24PRyt{}Y_<~zm{!I7{k z-TyhPCVdq%w8mQ|@4+o{U~zg36OHww8g4Osy4pD-bA?P|~HIJ0>2R!J+Y*S}z>?`pDq zI}mX!V+WjJdF*{=n9dp1!4taE1+osFGAtDiM=qnPdn%5}JY8~djAf9V^oatkjEpu( ztCM}QCQzZL`wg`Z^X?baf6%UiSz6N=Q0yG7_3Yin4b0Py;U*Sx2x;69VX*{ZtrTGi qw{r-W9PX4LY?L5WagVB2a32ro2|T2qONb1QvTj#tKS}>T#>#Ii;g;e6 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapMod.class new file mode 100644 index 0000000000000000000000000000000000000000..a67b09accb67e11c01cc57b49c7678397b52b594 GIT binary patch literal 5591 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 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ChatMessage.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ChatMessage.class new file mode 100644 index 0000000000000000000000000000000000000000..ddbaeb65c17c6ba35fda126dbf1770b28405d366 GIT binary patch literal 2470 zcmeHJ-D?v;5TA{0(p+nsM(g*-J_Kof$Td=YP!Tj%3`7m27JMq3yGgS3Zui{X1oD>& z3i|FJCC=qiTQG*SAXxC>W^Z>scJ{Y-^PBHKK7R#(9k^S79D{9bdVV-ku}J(bz2iac zp!V>f=D!%PwW2{!s!F{t+-tN}^soR63`&P$DEvsMp8vLUh=F5Jh%-Hda`RZ*c1Eh+ zV+MJPDn!HJep8|IW2rDOqU(HAPP&nwL}G--Z;glD@mlKT`qEam8Qg2mlV|GjL@Mc? zGT7Rj&t+?$!9rb!bg(rBYsabY8l%k;th2)6J7v)7Xow*XbezaYIH?s+jP67j+eh3h z?)8zoQG%TEtY&ytGYIQCj9Qk>q zgAZ*d98Qgq;WE>RJI%!yP_KAC&4$ncQ z5-N0A!~<7PB+bZ5V~VP>U1Qvp0p=(%h?ScOX~undv_C0^lf{LU))_pQi=j_8A4Kq8TMtZtFNp>jtY^?pL&KGEd>2L>0YPfDLf$dd#pz!E^I1V!*@#vn&vtk8IoW;tlHXZjM(OX>Y+D$o&NIlRJh0 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ForgeCommandSender.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$ForgeCommandSender.class new file mode 100644 index 0000000000000000000000000000000000000000..f5329fe408e5ecd8e2dec93bf0b70e5c612fc8b9 GIT binary patch literal 3108 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 KF9KKK8jx>-KPw~v literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TaskRecord.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TaskRecord.class new file mode 100644 index 0000000000000000000000000000000000000000..dc47f0f7d498ea6f95f4c0c82c891f7e1c23092b GIT binary patch literal 2659 zcmeHJTTc@~6h2d+OV=tF!TV)URPdpjCdLOLB#Ihq8YQ-p_(Z0&(=xC-Q)gyL_+=)V z=(|74c=m!NS}Ty4Na91MyXW%FIcIn0`@Vkr_!$5mz&#I21Ojb(LAbAC)(^UPEw<`g z^@m&a;Av*th?1UEi*06i-w3Wv=)pLF=^eJmf{3YJ@Vc`jxbvV)U~XVt*Kwa2)`o!s=*Ggp_0HGGb0@Mf*l$ z3$cAf{YraVP`BR~6o*vH2Ug2xgw4iUJ>Fv|(o(5HX>d@{KDxRB zd*rxR*XS7U-zVq`X^ltBTH2M7u=##6?rY;l?AVmn2_?*soqvbW7bce0Vte)&Sy*t0 zRhy#aCz0m6EytV~ijmJ{WDs?lvTUG!<#dMcF(cDA{Iu4ezRah!_7lwh1S$W>g#N}u z@+%vIbKnJ2VKnp*IsBC#Dd9hEP9q1;><2fDJgHA~03HYWNU=W5z=bNDgNp>_mvWxX zIKJhKRK1myNT-@oiC0P75vHBf`)pHl7Ht-XVPPD1+tL!KH;<_hqoke>IAI}t8rZWkb z<^lMhVCLQI`42GvC5yX+YdP}}xQy4;JPNMBRlFl`4X)#QfKWnPj&S2w1kxCSFord! UH7*o&-GbXG24dm>;4b*z0W*$Y^#A|> literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TexturesPayload.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$TexturesPayload.class new file mode 100644 index 0000000000000000000000000000000000000000..630511c9b56c275a773f8bb02917b351fd639faf GIT binary patch literal 2721 zcmeGeT~8B1a2CO%M}<ZW@F0XlQDaTTCPkvYknP>JEbi@|dwazGG80Yo z-5+I~yDK6elB0=$Z7@eeW5d|LG+iz1p7 zG|KFWAk@YlW3eG3%M~|ASpJiSkefh65r!9?Knfl9(QDHW`LWdA`<7*vAE_hD%dt$< zYRb}tx`orpzQc@Q_<5XG|C5jTwE8|k?gtS0M>6y`isTlWdwt+JQ@%X1iTwVT<`Vd? zHyb0nHTyLij_uR~HNcao&c*A(6kIJr8LknSnms(^lR-Ms5Q?wGL7SWPr1>R#hxVA< z=r_au<8U(82`uKC&6H{n)Fr|CHjw-J_6 P!yUK__mGDmoCWd|+nj?@ literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$WorldTracker.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/DynmapPlugin$WorldTracker.class new file mode 100644 index 0000000000000000000000000000000000000000..c1b325f38dbbe4a0b0bf760e3dd2453d7fb76e06 GIT binary patch literal 3932 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 c;Q1waiacszOjzqo7I8~^|S literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$EmptyChunk.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$EmptyChunk.class new file mode 100644 index 0000000000000000000000000000000000000000..8ed91fa3a53e684efda8c0407d6f11c72a8ff1d4 GIT binary patch literal 5201 zcmd^DTW=#Z6h2PVO&UVGEqei$i-9d9%aW)P7l|UIC7X&!SwN($=)P3$jFT~)v4ibY zjL--&E83G?E-KqM0 z8L@7)jdOmycf9xU@m}@7EuOG$E$GS98uJ2vzaDjMKVIh`OJH{zYfRFTtZM>g3EcQh zNMS!GuvRKRC6KKtpXXqMz}64!j8#J>JJq_@O6TDPviZw*QgfriGwxGQMO_gxE0m;N ztxiH7nNMk9<1pYb{ETVAF#i7|g;sC!(h^+$Mcf9;ryXv;FO}n+?{@uU)u@amLew*uwP##w z;d4x1m&lmcm=n0?Os~szoD~Utk|sk@M^`86*(yaXn1s*!J1%U8=VM5Jyd`!1#^xRqH zK6IX(dI-&B!}14FS`_h!s!JfBD+Ggd3M&nOUI6%X8cx*}>Vj{6(n%d3do1>##}ScBwgRXOr@_@7mq1rjY!r zUUb6sq3d7>>@=0f!l&mQ&H2Zy4TK@^Npo6LJ$bVp>Bsl6t#&u@xW{hf;SPbfX7|z9 zMkhSa2o#a^;wT|-x727f%FXdzyq|}A1b&_k8@K1KIi~!;A|ak;Fyaac-C+RQ1~74M>~aIq4+1RGO&fCqXDSEO&p5}6>P%} zt_ZvYw{V<9xIKxmmPtjpy8uEi1z{UrhF3;($f90}6|bh~P)tR54PH-0NV8%)72%Bq z5bmTRycx%4;7J#w#ELW>?xi4L4ljT}QxV>VcSih>nW@8W3c?!PN1hDq!n=+au?V~e NWt=_4brx3-{sCDIaxnk^ literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$OurEndMapIterator.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache$OurEndMapIterator.class new file mode 100644 index 0000000000000000000000000000000000000000..56f97d4c1927b3f7b8fa15e82a05b1ce9eabcbb5 GIT binary patch literal 4546 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 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeMapChunkCache.class new file mode 100644 index 0000000000000000000000000000000000000000..7f0a81b086895367ca4cc28d87284d46859652a7 GIT binary patch literal 8763 zcmd^E-E$jP6+bIWk*(TJ6I#+FZP`#4q!6q+qz$b@+E}q0fn&Rt>eerAQu}2}5S$v-j>fzkANP z=bXFu=x_i23VocWghm%#Z#QopICZ0u-@!Ah%vI)Is?6n=CnP7`_45 z5j=!O*GHsWbn z3Mr>eK@VWJVba`Z^@h|o?K?)pbg*L+^2zW#1GCxfsIpx_ALs{Gzvjw~9G7C)SXg+W zODfO?VO4a*EyJ%N3Bdk@Z`7H?;H|3bDgUX($F{c@S`I~?A&IuTz)ZJZcb)u#bbJ7G ziu)x*8h4!)S1bm`00lX9qUrd80ppo2h(MnOxj7}ghAvZT{fclaRkda}|KTN5i?`Rx zn;Kp1>zukNK{>$1EM@Hn?p_hw2T_MbOn>(K8uZ zpw}nq7RhwJChOL9jh@T~QK(8(NqF4ZozHDxjb&7um1e!lys`lRjZQDSreSXw9+%^A zax9zM7^f9&`genu_p$y&xyE!}Z@8Y&QLJWzGCb=s*y}NGHmF+RrlOGQrr|iQ(5p;u zS@uATbp^I&2(z|in5ZlVeNk#0wZ}7S2fDkX3#cXz8cbhpdWgwZ_0hSnbH{WYAC-847e#<+aSsu1AHcL7SnrtFwjIx{V|=fPca}bD+J^7z zJ4i=AGckm7bm8iTvf`0p-wUJV|45M}gc|Dj z=x+&RuTl%DK+2n}V!(GQ}yUBnR;+Z@w>bYJc*$$qNgv7irI3R_be*?mP~6pvb(>$^=YaHZ5tYqZYH`Ejaf^w%L88Y5PW zhg{8rZql-ASS*MQgf03t=>BAEOsXBhR5GjDOPO{EVQz~1H(iHGS==|27&Zo-#;`dz z*>s{1CuK7!?BK};NC1j15&}g@gzsA2EMUp_Yjs(Im_^V=TG;KTYKsYBcN+N@G**|m zZL7X2x~N@|(H85(+u>fU>Tdxn4BvHh=Mlrt#{l*AU!~-9TZ>TeRI4mV)5|!X;lOlu z3;$#^kHb(^3^Y2I?J_Zlo>rGWQ*xW0$zU0DITLdsmrXy7CX0^4yn<{EF+WY;)#$~D z*Sfm9hcO}zOawuvo0+B`Xmo7^95e^Ik9TD4p}92uNTZiWLydefz|-A5h&26VG;2G} zR#@8^r0J*V;P=z3RR|81g*5#PK{AvW!n*~7dXlDJYLt%%4ilvKp{4gX1iC=TMEsaF zLm5iZ2|Q&eP2-AAgftV<#}rM_Bu(Lcf~M)DqL0%lMNiXb6n%o81nOti^BM3xMQ7XN za~Piw`JN8xGa-F8q@N3^uJlPjN2ynWJ|EKOLOMgwtNAZP*83v(vOVKm$bT`!{SsYL za$lxdMf3E6qH}av(XZgAJ)(2;BE1Bfp{w*&HGY|1QFNZJDf%@NLF>>e*sGG#Tp|tBeC(50q%yr3+VEh6f5adpXoKF0 zASBw>#CUJ)U$F>p)8?=U$=72MwrD$oFwhUlcVZE~N$IYT47AB*5pBZ4s4ijQLv?vXb%!mncy{1Fg7 zjYSaDjOdUW=*i#2BJ9!q2*Ti){Cx~Ug7&d$f)40i(95Xg-vQqDa7KI|^oMx!9_ah{ Z|6~0B0P`QxNAz=4_+Ox={_3@}{{cRY`YZqd literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeWorld.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/ForgeWorld.class new file mode 100644 index 0000000000000000000000000000000000000000..cacc711cf269029340904f7cc424252bf22d5ec3 GIT binary patch literal 7361 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 Z4k`puwG@@~(I1MO1vbrZQE>*3egoG8$@>5R literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheHashMap.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheHashMap.class new file mode 100644 index 0000000000000000000000000000000000000000..35a971dae9f74893e09a13a94a14c3d073e80f58 GIT binary patch literal 2176 zcmb_d-%}e^6#i}!vLq}Lpap7a!5SbxTGCWRYA8h!Y?QW^I5=f=m}PSbTf!!@yI}@r z{6lA14qXdJeh0v9)5sFkbI_AKutP%H@~Dpt)lyp|&c zzIpYR%k8JSVb=g^2SbfTYWLEqYrTn z7X&W6638Sba8c<_3Pdw)#mhQ0^a~8JR`Oo!Xiqwy8+&!)JhIs^jrz9XSn3>H##HhG z3%#Y$MGoQ+B>|X?Baa%#a=l)auD5P`&WXT8CR9~2Ded2vvO5A_p7|6mbx@rD5>vat z*_|Y@%jn1=N4Q<Sou>1`YJ7In^LvLUkE-+LrY zucYCIz^gRA4qh%kP08kTYUq zwspg0FP=XWclIceAM0koNwdR(hi56hmWpsNR6N5xB-R1r`VG^RuHSGEdhDKV+6CGB z1L@zBrDMJ*u=T&phoE@by6WL%rO|RsxncP&)tN7Tx#tzN1ilJc=o*->2~fRk+tOKM zxn1dM_=FI5ahh}4Z#E5g1!j7ptAHLJ`)q{{e>FT1DE4F|-2sEN7Z?bg@D{r=%3o0w z5jEB*MbwnyZNit6sgxRslw-Ub#2Z|PcrtlbdQ-XlDN?yqYGfq#6r;JxQ;g+)!^Fd< zc=HsOe)f$_aUG+1V3z9~jbt#&NpKlCO#5#Uu2T%=3U?*F%}^qAk;V-7b||yH?)Uje`;7Y*68`|(1~KaZ literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheRec.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/SnapshotCache$CacheRec.class new file mode 100644 index 0000000000000000000000000000000000000000..70d47a8ccf6caf669a959af546122f540ac93f75 GIT binary patch literal 835 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 literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/FilePermissions.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/FilePermissions.class new file mode 100644 index 0000000000000000000000000000000000000000..86646aa42efbe8e2caeacacef52ecd52f9a03359 GIT binary patch literal 4121 zcma)8YgZfB72Q`z49J5WuuTwP8)6%SKvw;Tv5|v=WhZtb1U1Aw>ed;=NFF0*6lvs` zrfu5vkw@FKNuRjyCvEDsZdU`PtksoI{?OmhPyHQUrTfkZF%mdsd0EW8_uO;#+56mc z@4x>0kADIq9Zf6^^~okR^ZG|+-w0j`OEi-Loih0eZ z>`7t@;s82ilum)hlJgR{m~?yqhgBSrj{R9no2^5aK=(@p3eZu7w}Zvip`}uKq0o`C zOgnEeg>Hlu^a$+sU`SXQ6}>VozhS1WAOs>3!Eu4kZZE%Ez!H(bo`hlQ$>RKso*&m{ zvXrGMVWqU}buDkmacNSd8Pz3gBOh{-N@oNPetZythA~@9&9N{YUqRHMUjjVqka}Ig z0J+*y#6g@xe?60VMumbpN!c{;d?7Ztof8a5qltg1+0m^_>MT}I!x;lXpJV+uwpj=FogB>GRP zxP~!-U`DsySyt8QZ3@tnQi8P?^XBMG1d+r`Dz0NfV3%zrtXK7XTq{tzN=T)Xtzc>k z!K5UJN%Zf+4awK1Deh*vGKgHgsp4g5R7snitvV}i2aEMgOTJPnW@Sk$x3of%)JW;l zDl)jmy}2T(I_pidwAXX;HoR?}s>5yO$eyDWvv#qd=Vk6$74s5Tt^P_;%NAsjSSofw zr8M>}gS0;FEy0f9bBjs#yfyE#IsJb( zZOclPqit>K@pXJt!8Zhsc=~EZ*<5li{LySNW0)%5#kZVX<%pC$w%EM8VnIfEPsRKA z4okx*jIg4%y?!XlB)+HO`}hHa@aPR?vjU+~`VK>JE1Ts5f_Dy2pltYyDn5tL({H|z zm7Qrj;;AsF1uk?aOx=#oQPi43SFEbn_7jakKfN|$dG5?}RHIWAiXI;l_% z{|DgXKVD}1uIH1|hams6Z4$?BUE4w0N8QeEb_-L*rD_-WBYBF}#e>Jp{yq zH5`}XHM@2IzE`yM?=W(H9!vqTQB@UQ%G znd!m|@+gp#ghIxo`^!B?54kO7^9ecaiJ zukp9WNnO^pJJY}!nQ%RPkI=3lzMD&Ll51b-AiOQhT^ypBWPF_1#}lO@N&Xe@aQy8> zs1X*=cPEzcH1S_#)dZH%5ek&6#vkqt`B(76MFb-uzf(J0bPD6&5qT!uG{Za%z%$ff z5DmD%x_*}A53x?-O!gALF5?QnuTc(J1*d6AOy#u4%W02O6_q(OQ2Tq-o)24|6q}^@ z%i7DG3N1FQUMo>?3jRmvpt9?PQ|ky$wc}-}+t*2rEA_@a8N!dsV$ac04S}^dWR*#h z3itMr1*xms1ezS(hM7i^M`45zuQ`czxckeAwUiTUDd{OSW9MpQ;2+~B{H01xp0dhX zdmrLS2LI_Qel0-7lHrIG*k2ubk^rU{cAA~%hNqNT%b{EGdwf9u^olIIg(N;#$Rz^|h QkWxkwB8oq8s}FzvAFF`r_W%F@ literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/OpPermissions.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/OpPermissions.class new file mode 100644 index 0000000000000000000000000000000000000000..f15a987f2846bbfd5a7c761349443f9f4dbea6a6 GIT binary patch literal 2427 zcma)7-%}e^6#gz**bq`EfkIPkn+nn-w5-rdYpK*qTU!l3G!%rYxP&Ea-Rx#|mri-{ z$v?tA`s9q$2X(ad$4no6@Ne-?aID|G8PZ(9puh>Cgdv0JXR2LgF9h$)Lhvp;Ga7?#sENncq zN}-MpfvKYeYlK?#y+`(@fz17Yq%vML7!MzZ8H$}T zOC>7^G9}Lqea}hYG|uWcBhcNXW5FvYk-<3uSPKw^RP`WhA-8WXu=c-;T9(JYKrMYbVPHbX>jEQ(DRxV> z&!V$aw%sHqafxMDRWpInLv?OmHJu>J8%a#zip+DcP(38iYqI3tOyU}3ZL{QVI*vfP zwtc10tmimvNH#7jPcbofWskMXj;6pp2h`L2zzNwQd&_0F`fte$?_JSSCV~D|31zDu znwsWTl}t5Bkd4GR0ynY+*9wga%cJC*TcKgOp&jlTRY&f|SX7VO$~GO*+Z#^mbT^56 zvNhwf!A#o?5_lhTItl{WqXti69t%8t%2pVm37pO5T63$>N#Fw%b$lo=)Dk6fOJWK4 z1#||UH!JMc{DH74ouTzOH0~{N)9Urm0_97g%B8fFTh;|XR6w^hx z7M-eB1Itxsa=!H@TP1);fXrQVUGUsw1{*y+&*PO2MQ_J1S$Ax8FzvZe#w87$c=5Tl#hIc{TqW-TB|pyZRh`KcRnY^;-LjDJ?eh4f@mV zrzhv~KcfG_GYqVJ855^ZXkWklGj-}`yz)KQ#_%e~GsFS9p)qz3e;FJ{j?YP?a0PuB z=GP2;iG>lo#;1Ja@kJ&XvOA9~`Q>OGK-)_Up+kqzk%v$tqX~@R0)3*LTvYtO;#}jJ zQ#I=IKVW?987@CT=S9-Jx^nS5e$^Dil=2*;8Yjsq&2dPv%106EtWM!Nxd<5*Z()WA zar)oj7{lANl3(rH&H6Wa=Sl8V`Y)W+*)Rg{V3wm)`40W$&f_(9Px;=aICUtTvrX#6 z>pbFh9&y}K>ImFstSIIewV0!0zhk(Top+z0U3(gVy!Q-?zkqic)p)8)Y#tN;*0 zV-9+#elH`BQ07K{M(Yel@i9J83`g;4JwF=SdQ-Hn*ST!hxTMKt;J@L!l@Q~EH!z5a XCfIZxHeH8JH^H)?2Z{Qb($4$`QK?c% literal 0 HcmV?d00001 diff --git a/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/PermissionProvider.class b/forge-1.16.2/bin/main/org/dynmap/forge_1_16_1/permissions/PermissionProvider.class new file mode 100644 index 0000000000000000000000000000000000000000..b646d1d8739878eb50997ff842af0727e487062a GIT binary patch literal 532 zcmah`%TB{E5FD4%(DEu0h)c4uGTRxbdq(Ra{d809A6QKd6c@R+a++`t_LE-$3XR9UKAm)!fop2?Rg zlEy)sp+6T(!Be3VK7E?Y&@${ii<04PFwxTTOlcV!5nC>`Rkq?qDk^EXSC{*m8&4V$ z-x{Tp@o>g)LOPA>V;)I{GvcOkoRVtOBDbr-rp70;uqx%gwBzC0o1yEgL<_qxBs}|9 z;AYd|c3pJqABNLyQ!`xr=~?IbD~g_ + ant.checksum file: task.archivePath +} + +afterEvaluate { +reobf { + shadowJar { + mappings = createMcpToSrg.output + } +} +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' +} + +artifacts { + archives deobfJar +} + +build.dependsOn(shadowJar) diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ChunkSnapshot.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ChunkSnapshot.java new file mode 100644 index 00000000..6b9ea4a4 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ChunkSnapshot.java @@ -0,0 +1,286 @@ +package org.dynmap.forge_1_16_2; + +import java.util.Arrays; + +import org.dynmap.Log; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.DataBitsPacked; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.util.BitArray; + +/** + * Represents a static, thread-safe snapshot of chunk of blocks + * Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering) + */ +public class ChunkSnapshot +{ + private static interface Section { + public DynmapBlockState getBlockType(int x, int y, int z); + public int getBlockSkyLight(int x, int y, int z); + public int getBlockEmittedLight(int x, int y, int z); + public boolean isEmpty(); + } + + private final int x, z; + private final Section[] section; + private final int[] hmap; // Height map + private final int[] biome; + private final long captureFulltime; + private final int sectionCnt; + private final long inhabitedTicks; + + private static final int BLOCKS_PER_SECTION = 16 * 16 * 16; + private static final int COLUMNS_PER_CHUNK = 16 * 16; + private static final byte[] emptyData = new byte[BLOCKS_PER_SECTION / 2]; + private static final byte[] fullData = new byte[BLOCKS_PER_SECTION / 2]; + + static + { + Arrays.fill(fullData, (byte)0xFF); + } + + private static class EmptySection implements Section { + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return DynmapBlockState.AIR; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + return 15; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) { + return 0; + } + @Override + public boolean isEmpty() { + return true; + } + } + + private static final EmptySection empty_section = new EmptySection(); + + private static class StdSection implements Section { + DynmapBlockState[] states; + byte[] skylight; + byte[] emitlight; + + public StdSection() { + states = new DynmapBlockState[BLOCKS_PER_SECTION]; + Arrays.fill(states, DynmapBlockState.AIR); + skylight = emptyData; + emitlight = emptyData; + } + @Override + public DynmapBlockState getBlockType(int x, int y, int z) { + return states[((y & 0xF) << 8) | (z << 4) | x]; + } + @Override + public int getBlockSkyLight(int x, int y, int z) { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public int getBlockEmittedLight(int x, int y, int z) + { + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[off] >> (4 * (x & 1))) & 0xF; + } + @Override + public boolean isEmpty() { + return false; + } + } + /** + * Construct empty chunk snapshot + * + * @param x + * @param z + */ + public ChunkSnapshot(int worldheight, int x, int z, long captime, long inhabitedTime) + { + this.x = x; + this.z = z; + this.captureFulltime = captime; + this.biome = new int[COLUMNS_PER_CHUNK]; + this.sectionCnt = worldheight / 16; + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + + /* Create empty height map */ + this.hmap = new int[16 * 16]; + + this.inhabitedTicks = inhabitedTime; + } + + public ChunkSnapshot(CompoundNBT nbt, int worldheight) { + this.x = nbt.getInt("xPos"); + this.z = nbt.getInt("zPos"); + this.captureFulltime = 0; + this.hmap = nbt.getIntArray("HeightMap"); + this.sectionCnt = worldheight / 16; + if (nbt.contains("InhabitedTime")) { + this.inhabitedTicks = nbt.getLong("InhabitedTime"); + } + else { + this.inhabitedTicks = 0; + } + /* Allocate arrays indexed by section */ + this.section = new Section[this.sectionCnt]; + /* Fill with empty data */ + for (int i = 0; i < this.sectionCnt; i++) { + this.section[i] = empty_section; + } + /* Get sections */ + ListNBT sect = nbt.getList("Sections", 10); + for (int i = 0; i < sect.size(); i++) { + CompoundNBT sec = sect.getCompound(i); + int secnum = sec.getByte("Y"); + if (secnum >= this.sectionCnt) { + //Log.info("Section " + (int) secnum + " above world height " + worldheight); + continue; + } + if (secnum < 0) + continue; + //System.out.println("section(" + secnum + ")=" + sec.asString()); + // Create normal section to initialize + StdSection cursect = new StdSection(); + this.section[secnum] = cursect; + DynmapBlockState[] states = cursect.states; + DynmapBlockState[] palette = null; + // If we've got palette and block states list, process non-empty section + if (sec.contains("Palette", 9) && sec.contains("BlockStates", 12)) { + ListNBT plist = sec.getList("Palette", 10); + long[] statelist = sec.getLongArray("BlockStates"); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + CompoundNBT tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.contains("Properties")) { + StringBuilder statestr = new StringBuilder(); + CompoundNBT prop = tc.getCompound("Properties"); + for (String pid : prop.keySet()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).getString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + int recsperblock = (4096 + statelist.length - 1) / statelist.length; + int bitsperblock = 64 / recsperblock; + BitArray db = null; + DataBitsPacked dbp = null; + try { + db = new BitArray(bitsperblock, 4096, statelist); + } catch (Exception x) { // Handle legacy encoded + bitsperblock = (statelist.length * 64) / 4096; + dbp = new DataBitsPacked(bitsperblock, 4096, statelist); + } + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + int v = (dbp != null) ? dbp.getAt(j) : db.getAt(j); + states[j] = DynmapBlockState.getStateByGlobalIndex(v); + } + } + else { + for (int j = 0; j < 4096; j++) { + int v = (dbp != null) ? dbp.getAt(j) : db.getAt(j); + states[j] = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + } + } + } + if (sec.contains("BlockLight")) { + cursect.emitlight = sec.getByteArray("BlockLight"); + } + if (sec.contains("SkyLight")) { + cursect.skylight = sec.getByteArray("SkyLight"); + } + } + /* Get biome data */ + this.biome = new int[COLUMNS_PER_CHUNK]; + if (nbt.contains("Biomes")) { + int[] bb = nbt.getIntArray("Biomes"); + if (bb != null) { + // If v1.15+ format + if (bb.length > COLUMNS_PER_CHUNK) { + // For now, just pad the grid with the first 16 + for (int i = 0; i < COLUMNS_PER_CHUNK; i++) { + int off = ((i >> 4) & 0xC) + ((i >> 2) & 0x3); + int bv = bb[off + 64]; // Offset to y=64 + if (bv < 0) bv = 0; + this.biome[i] = bv; + } + } + else { // Else, older chunks + for (int i = 0; i < bb.length; i++) { + int bv = bb[i]; + if (bv < 0) bv = 0; + this.biome[i] = bv; + } + } + } + } + } + + public int getX() + { + return x; + } + + public int getZ() + { + return z; + } + + public DynmapBlockState getBlockType(int x, int y, int z) + { + return section[y >> 4].getBlockType(x, y, z); + } + + public int getBlockSkyLight(int x, int y, int z) + { + return section[y >> 4].getBlockSkyLight(x, y, z); + } + + public int getBlockEmittedLight(int x, int y, int z) + { + return section[y >> 4].getBlockEmittedLight(x, y, z); + } + + public int getHighestBlockYAt(int x, int z) + { + return hmap[z << 4 | x]; + } + + public int getBiome(int x, int z) + { + return biome[z << 4 | x]; + } + + public final long getCaptureFullTime() + { + return captureFulltime; + } + + public boolean isSectionEmpty(int sy) + { + return section[sy].isEmpty(); + } + + public long getInhabitedTicks() { + return inhabitedTicks; + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ClientProxy.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ClientProxy.java new file mode 100644 index 00000000..ba6b32cb --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ClientProxy.java @@ -0,0 +1,6 @@ +package org.dynmap.forge_1_16_2; + +public class ClientProxy extends Proxy { + public ClientProxy() { + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapMod.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapMod.java new file mode 100644 index 00000000..5a92e453 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapMod.java @@ -0,0 +1,127 @@ +package org.dynmap.forge_1_16_2; + +import java.io.File; + +import org.dynmap.DynmapCommonAPI; +import org.dynmap.DynmapCommonAPIListener; +import org.dynmap.Log; +import org.dynmap.forge_1_16_2.DynmapPlugin.OurLog; + +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; +import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; +import net.minecraftforge.fml.event.server.FMLServerStartedEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +@Mod("dynmap") +public class DynmapMod +{ + // The instance of your mod that Forge uses. + public static DynmapMod instance; + + // Says where the client and server 'proxy' code is loaded. + public static Proxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> Proxy::new); + + public static DynmapPlugin plugin; + public static File jarfile; + public static String ver; + public static boolean useforcedchunks; + + public class APICallback extends DynmapCommonAPIListener { + @Override + public void apiListenerAdded() { + if(plugin == null) { + plugin = proxy.startServer(server); + } + } + @Override + public void apiEnabled(DynmapCommonAPI api) { + } + } + + //TODO + //public class LoadingCallback implements net.minecraftforge.common.ForgeChunkManager.LoadingCallback { + // @Override + // public void ticketsLoaded(List tickets, World world) { + // if(tickets.size() > 0) { + // DynmapPlugin.setBusy(world, tickets.get(0)); + // for(int i = 1; i < tickets.size(); i++) { + // ForgeChunkManager.releaseTicket(tickets.get(i)); + // } + // } + // } + //} + + public DynmapMod() { + instance = this; + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::init); + + MinecraftForge.EVENT_BUS.register(this); + + Log.setLogger(new OurLog()); + org.dynmap.modsupport.ModSupportImpl.init(); + } + + public void setup(final FMLCommonSetupEvent event) + { + //TOOO + jarfile = ModList.get().getModFileById("dynmap").getFile().getFilePath().toFile(); + + ver = ModList.get().getModContainerById("dynmap").get().getModInfo().getVersion().toString(); + + //// Load configuration file - use suggested (config/WesterosBlocks.cfg) + //Configuration cfg = new Configuration(event.getSuggestedConfigurationFile()); + //try { + // cfg.load(); + // + // useforcedchunks = cfg.get("Settings", "UseForcedChunks", true).getBoolean(true); + //} + //finally + //{ + // cfg.save(); + //} + } + + public void init(FMLLoadCompleteEvent event) + { + /* Set up for chunk loading notice from chunk manager */ + //TODO + //if(useforcedchunks) { + // ForgeChunkManager.setForcedChunkLoadingCallback(DynmapMod.instance, new LoadingCallback()); + //} + //else { + // System.out.println("[Dynmap] World loading using forced chunks is disabled"); + //} + } + + private MinecraftServer server; + + @SubscribeEvent + public void onServerStarting(FMLServerStartingEvent event) { + server = event.getServer(); + if(plugin == null) + plugin = proxy.startServer(server); + plugin.onStarting(server.getCommandManager().getDispatcher()); + } + + @SubscribeEvent + public void onServerStarted(FMLServerStartedEvent event) { + DynmapCommonAPIListener.register(new APICallback()); + plugin.serverStarted(); + } + + @SubscribeEvent + public void serverStopping(FMLServerStoppingEvent event) + { + proxy.stopServer(plugin); + plugin = null; + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapPlugin.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapPlugin.java new file mode 100644 index 00000000..3456573d --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/DynmapPlugin.java @@ -0,0 +1,2016 @@ +package org.dynmap.forge_1_16_2; + +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.PriorityQueue; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.regex.Pattern; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.FlowingFluidBlock; +import net.minecraft.block.material.Material; +import net.minecraft.command.CommandException; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.ServerPlayNetHandler; +import net.minecraft.network.play.server.STitlePacket; +import net.minecraft.particles.IParticleData; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.BanList; +import net.minecraft.server.management.IPBanList; +import net.minecraft.server.management.PlayerProfileCache; +import net.minecraft.util.ObjectIntIdentityMap; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.text.ChatType; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.IWorld; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.IChunk; +import net.minecraft.world.server.ChunkHolder; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.ServerChatEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent; +import net.minecraftforge.event.entity.player.PlayerEvent.PlayerRespawnEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ChunkDataEvent; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.ModContainer; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.RegistryManager; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.forgespi.language.IModInfo; + +import org.apache.commons.codec.Charsets; +import org.apache.commons.codec.binary.Base64; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.dynmap.ConfigurationNode; +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapCommonAPIListener; +import org.dynmap.DynmapCore; +import org.dynmap.DynmapLocation; +import org.dynmap.DynmapWorld; +import org.dynmap.Log; +import org.dynmap.MapManager; +import org.dynmap.PlayerList; +import org.dynmap.common.BiomeMap; +import org.dynmap.common.DynmapCommandSender; +import org.dynmap.common.DynmapPlayer; +import org.dynmap.common.DynmapServerInterface; +import org.dynmap.common.DynmapListenerManager.EventType; +import org.dynmap.debug.Debug; +import org.dynmap.forge_1_16_2.DmapCommand; +import org.dynmap.forge_1_16_2.DmarkerCommand; +import org.dynmap.forge_1_16_2.DynmapCommand; +import org.dynmap.forge_1_16_2.DynmapMod; +import org.dynmap.forge_1_16_2.permissions.FilePermissions; +import org.dynmap.forge_1_16_2.permissions.OpPermissions; +import org.dynmap.forge_1_16_2.permissions.PermissionProvider; +import org.dynmap.permissions.PermissionsHandler; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.utils.DynIntHashMap; +import org.dynmap.utils.DynmapLogger; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.VisibilityLimit; + +import com.google.common.collect.Iterables; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSortedSet; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public class DynmapPlugin +{ + private DynmapCore core; + private PermissionProvider permissions; + private boolean core_enabled; + public SnapshotCache sscache; + public PlayerList playerList; + private MapManager mapManager; + private static net.minecraft.server.MinecraftServer server; + public static DynmapPlugin plugin; + private ChatHandler chathandler; + private HashMap sortWeights = new HashMap(); + // Drop world load ticket after 30 seconds + private long worldIdleTimeoutNS = 30 * 1000000000L; + private HashMap worlds = new HashMap(); + private IWorld last_world; + private ForgeWorld last_fworld; + private Map players = new HashMap(); + //TODO private ForgeMetrics metrics; + private HashSet modsused = new HashSet(); + private ForgeServer fserver = new ForgeServer(); + private boolean tickregistered = false; + // TPS calculator + private double tps; + private long lasttick; + private long avgticklen; + // Per tick limit, in nsec + private long perTickLimit = (50000000); // 50 ms + private boolean useSaveFolder = true; + + private static final int SIGNPOST_ID = 63; + private static final int WALLSIGN_ID = 68; + + private static final String[] TRIGGER_DEFAULTS = { "blockupdate", "chunkpopulate", "chunkgenerate" }; + + private static final Pattern patternControlCode = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); + + public static class BlockUpdateRec { + IWorld w; + String wid; + int x, y, z; + } + ConcurrentLinkedQueue blockupdatequeue = new ConcurrentLinkedQueue(); + + public static DynmapBlockState[] stateByID; + + private Map knownloadedchunks = new HashMap(); + private boolean didInitialKnownChunks = false; + private void addKnownChunk(ForgeWorld fw, ChunkPos pos) { + LongOpenHashSet cset = knownloadedchunks.get(fw.getName()); + if (cset == null) { + cset = new LongOpenHashSet(); + knownloadedchunks.put(fw.getName(), cset); + } + cset.add(pos.asLong()); + } + private void removeKnownChunk(ForgeWorld fw, ChunkPos pos) { + LongOpenHashSet cset = knownloadedchunks.get(fw.getName()); + if (cset != null) { + cset.remove(pos.asLong()); + } + } + private boolean checkIfKnownChunk(ForgeWorld fw, ChunkPos pos) { + LongOpenHashSet cset = knownloadedchunks.get(fw.getName()); + if (cset != null) { + return cset.contains(pos.asLong()); + } + return false; + } + + private static Registry reg = null; + + private static Registry getBiomeReg() { + if (reg == null) { + reg = server.func_244267_aX().func_243612_b(Registry.field_239720_u_); + } + return reg; + } + + /** + * Initialize block states (org.dynmap.blockstate.DynmapBlockState) + */ + public void initializeBlockStates() { + stateByID = new DynmapBlockState[512*32]; // Simple map - scale as needed + Arrays.fill(stateByID, DynmapBlockState.AIR); // Default to air + + ObjectIntIdentityMap bsids = Block.BLOCK_STATE_IDS; + + DynmapBlockState basebs = null; + Block baseb = null; + int baseidx = 0; + + Iterator iter = bsids.iterator(); + while (iter.hasNext()) { + BlockState bs = iter.next(); + int idx = bsids.getId(bs); + if (idx >= stateByID.length) { + int plen = stateByID.length; + stateByID = Arrays.copyOf(stateByID, idx+1); + Arrays.fill(stateByID, plen, stateByID.length, DynmapBlockState.AIR); + } + Block b = bs.getBlock(); + // If this is new block vs last, it's the base block state + if (b != baseb) { + basebs = null; + baseidx = idx; + baseb = b; + } + + ResourceLocation ui = b.getRegistryName(); + if (ui == null) { + continue; + } + String bn = ui.getNamespace() + ":" + ui.getPath(); + // Only do defined names, and not "air" + if (!bn.equals(DynmapBlockState.AIR_BLOCK)) { + Material mat = bs.getMaterial(); + String statename = ""; + for(net.minecraft.state.Property p : bs.func_235904_r_()) { + if (statename.length() > 0) { + statename += ","; + } + statename += p.getName() + "=" + bs.get(p).toString(); + } + //Log.info("bn=" + bn + ", statenme=" + statename + ",idx=" + idx + ",baseidx=" + baseidx); + DynmapBlockState dbs = new DynmapBlockState(basebs, idx - baseidx, bn, statename, mat.toString(), idx); + stateByID[idx] = dbs; + if (basebs == null) { basebs = dbs; } + if (mat.isSolid()) { + dbs.setSolid(); + } + if (mat == Material.AIR) { + dbs.setAir(); + } + if (mat == Material.WOOD) { + dbs.setLog(); + } + if (mat == Material.LEAVES) { + dbs.setLeaves(); + } + if ((!bs.getFluidState().isEmpty()) && !(bs.getBlock() instanceof FlowingFluidBlock)) { + dbs.setWaterlogged(); + } + } + } + for (int gidx = 0; gidx < DynmapBlockState.getGlobalIndexMax(); gidx++) { + DynmapBlockState bs = DynmapBlockState.getStateByGlobalIndex(gidx); + //Log.info(gidx + ":" + bs.toString() + ", gidx=" + bs.globalStateIndex + ", sidx=" + bs.stateIndex); + } + } + + public static final Item getItemByID(int id) { + return Item.getItemById(id); + } + + private static Biome[] biomelist = null; + + public static final Biome[] getBiomeList() { + if (biomelist == null) { + biomelist = new Biome[256]; + Iterator iter = getBiomeReg().iterator(); + while (iter.hasNext()) { + Biome b = iter.next(); + int bidx = getBiomeReg().getId(b); + if (bidx >= biomelist.length) { + biomelist = Arrays.copyOf(biomelist, bidx + biomelist.length); + } + biomelist[bidx] = b; + } + } + return biomelist; + } + public static final NetworkManager getNetworkManager(ServerPlayNetHandler nh) { + return nh.netManager; + } + + private ForgePlayer getOrAddPlayer(PlayerEntity p) { + String name = p.getEntity().getName().getString(); + ForgePlayer fp = players.get(name); + if(fp != null) { + fp.player = p; + } + else { + fp = new ForgePlayer(p); + players.put(name, fp); + } + return fp; + } + + private static class TaskRecord implements Comparable + { + private long ticktorun; + private long id; + private FutureTask future; + @Override + public int compareTo(Object o) + { + TaskRecord tr = (TaskRecord)o; + + if (this.ticktorun < tr.ticktorun) + { + return -1; + } + else if (this.ticktorun > tr.ticktorun) + { + return 1; + } + else if (this.id < tr.id) + { + return -1; + } + else if (this.id > tr.id) + { + return 1; + } + else + { + return 0; + } + } + } + + private class ChatMessage { + String message; + PlayerEntity sender; + } + private ConcurrentLinkedQueue msgqueue = new ConcurrentLinkedQueue(); + + public class ChatHandler { + @SubscribeEvent + public void handleChat(ServerChatEvent event) { + String msg = event.getMessage(); + if(!msg.startsWith("/")) { + ChatMessage cm = new ChatMessage(); + cm.message = msg; + cm.sender = event.getPlayer(); + msgqueue.add(cm); + } + } + } + + /** TODO: depends on forge chunk manager + private static class WorldBusyRecord { + long last_ts; + Ticket ticket; + } + private static HashMap busy_worlds = new HashMap(); + + private void setBusy(World w) { + setBusy(w, null); + } + static void setBusy(World w, Ticket t) { + if(w == null) return; + if (!DynmapMod.useforcedchunks) return; + WorldBusyRecord wbr = busy_worlds.get(w.provider.getDimension()); + if(wbr == null) { // Not busy, make ticket and keep spawn loaded + Debug.debug("World " + w.getWorldInfo().getWorldName() + "/"+ w.provider.getDimensionType().getName() + " is busy"); + wbr = new WorldBusyRecord(); + if(t != null) + wbr.ticket = t; + else + wbr.ticket = ForgeChunkManager.requestTicket(DynmapMod.instance, w, ForgeChunkManager.Type.NORMAL); + if(wbr.ticket != null) { + BlockPos cc = w.getSpawnPoint(); + ChunkPos ccip = new ChunkPos(cc.getX() >> 4, cc.getZ() >> 4); + ForgeChunkManager.forceChunk(wbr.ticket, ccip); + busy_worlds.put(w.provider.getDimension(), wbr); // Add to busy list + } + } + wbr.last_ts = System.nanoTime(); + } + + private void doIdleOutOfWorlds() { + if (!DynmapMod.useforcedchunks) return; + long ts = System.nanoTime() - worldIdleTimeoutNS; + for(Iterator itr = busy_worlds.values().iterator(); itr.hasNext();) { + WorldBusyRecord wbr = itr.next(); + if(wbr.last_ts < ts) { + World w = wbr.ticket.world; + Debug.debug("World " + w.getWorldInfo().getWorldName() + "/" + wbr.ticket.world.provider.getDimensionType().getName() + " is idle"); + if (wbr.ticket != null) + ForgeChunkManager.releaseTicket(wbr.ticket); // Release hold on world + itr.remove(); + } + } + } + */ + + public static class OurLog implements DynmapLogger { + Logger log; + public static final String DM = "[Dynmap] "; + OurLog() { + log = LogManager.getLogger("Dynmap"); + } + @Override + public void info(String s) { + log.info(DM + s); + } + + @Override + public void severe(Throwable t) { + log.fatal(t); + } + + @Override + public void severe(String s) { + log.fatal(DM + s); + } + + @Override + public void severe(String s, Throwable t) { + log.fatal(DM + s, t); + } + + @Override + public void verboseinfo(String s) { + log.info(DM + s); + } + + @Override + public void warning(String s) { + log.warn(DM + s); + } + + @Override + public void warning(String s, Throwable t) { + log.warn(DM + s, t); + } + } + + public DynmapPlugin(MinecraftServer srv) + { + plugin = this; + this.server = srv; + } + + public boolean isOp(String player) { + String[] ops = server.getPlayerList().getOppedPlayers().getKeys(); + for (String op : ops) { + if (op.equalsIgnoreCase(player)) { + return true; + } + } + return (server.isSinglePlayer() && player.equalsIgnoreCase(server.getServerOwner())); + } + + private boolean hasPerm(PlayerEntity psender, String permission) { + PermissionsHandler ph = PermissionsHandler.getHandler(); + if((psender != null) && ph.hasPermission(psender.getEntity().getName().getString(), permission)) { + return true; + } + return permissions.has(psender, permission); + } + + private boolean hasPermNode(PlayerEntity psender, String permission) { + PermissionsHandler ph = PermissionsHandler.getHandler(); + if((psender != null) && ph.hasPermissionNode(psender.getEntity().getName().getString(), permission)) { + return true; + } + return permissions.hasPermissionNode(psender, permission); + } + + private Set hasOfflinePermissions(String player, Set perms) { + Set rslt = null; + PermissionsHandler ph = PermissionsHandler.getHandler(); + if(ph != null) { + rslt = ph.hasOfflinePermissions(player, perms); + } + Set rslt2 = hasOfflinePermissions(player, perms); + if((rslt != null) && (rslt2 != null)) { + Set newrslt = new HashSet(rslt); + newrslt.addAll(rslt2); + rslt = newrslt; + } + else if(rslt2 != null) { + rslt = rslt2; + } + return rslt; + } + private boolean hasOfflinePermission(String player, String perm) { + PermissionsHandler ph = PermissionsHandler.getHandler(); + if(ph != null) { + if(ph.hasOfflinePermission(player, perm)) { + return true; + } + } + return permissions.hasOfflinePermission(player, perm); + } + + /** + * Server access abstraction class + */ + public class ForgeServer extends DynmapServerInterface + { + /* Server thread scheduler */ + private Object schedlock = new Object(); + private long cur_tick; + private long next_id; + private long cur_tick_starttime; + private PriorityQueue runqueue = new PriorityQueue(); + + public ForgeServer() { + } + + private GameProfile getProfileByName(String player) { + PlayerProfileCache cache = server.getPlayerProfileCache(); + return cache.getGameProfileForUsername(player); + } + + @Override + public int getBlockIDAt(String wname, int x, int y, int z) { + return -1; + } + + @Override + public int isSignAt(String wname, int x, int y, int z) { + return -1; + } + + @Override + public void scheduleServerTask(Runnable run, long delay) + { + TaskRecord tr = new TaskRecord(); + tr.future = new FutureTask(run, null); + + /* Add task record to queue */ + synchronized (schedlock) + { + tr.id = next_id++; + tr.ticktorun = cur_tick + delay; + runqueue.add(tr); + } + } + @Override + public DynmapPlayer[] getOnlinePlayers() + { + if(server.getPlayerList() == null) + return new DynmapPlayer[0]; + List playlist = server.getPlayerList().getPlayers(); + int pcnt = playlist.size(); + DynmapPlayer[] dplay = new DynmapPlayer[pcnt]; + + for (int i = 0; i < pcnt; i++) + { + PlayerEntity p = (PlayerEntity)playlist.get(i); + dplay[i] = getOrAddPlayer(p); + } + + return dplay; + } + @Override + public void reload() + { + plugin.onDisable(); + plugin.onEnable(); + plugin.onStart(); + } + @Override + public DynmapPlayer getPlayer(String name) + { + List players = server.getPlayerList().getPlayers(); + + for (Object o : players) + { + PlayerEntity p = (PlayerEntity)o; + + if (p.getEntity().getName().getString().equalsIgnoreCase(name)) + { + return getOrAddPlayer(p); + } + } + + return null; + } + @Override + public Set getIPBans() + { + IPBanList bl = server.getPlayerList().getBannedIPs(); + Set ips = new HashSet(); + + for (String s : bl.getKeys()) { + ips.add(s); + } + + return ips; + } + @Override + public Future callSyncMethod(Callable task) { + return callSyncMethod(task, 0); + } + public Future callSyncMethod(Callable task, long delay) + { + TaskRecord tr = new TaskRecord(); + FutureTask ft = new FutureTask(task); + tr.future = ft; + + /* Add task record to queue */ + synchronized (schedlock) + { + tr.id = next_id++; + tr.ticktorun = cur_tick + delay; + runqueue.add(tr); + } + + return ft; + } + @Override + public String getServerName() + { + String sn; + if (server.isSinglePlayer()) + sn = "Integrated"; + else + sn = server.getServerHostname(); + if(sn == null) sn = "Unknown Server"; + return sn; + } + @Override + public boolean isPlayerBanned(String pid) + { + BanList bl = server.getPlayerList().getBannedPlayers(); + return bl.isBanned(getProfileByName(pid)); + } + + @Override + public String stripChatColor(String s) + { + return patternControlCode.matcher(s).replaceAll(""); + } + private Set registered = new HashSet(); + @Override + public boolean requestEventNotification(EventType type) + { + if (registered.contains(type)) + { + return true; + } + + switch (type) + { + case WORLD_LOAD: + case WORLD_UNLOAD: + /* Already called for normal world activation/deactivation */ + break; + + case WORLD_SPAWN_CHANGE: + /*TODO + pm.registerEvents(new Listener() { + @EventHandler(priority=EventPriority.MONITOR) + public void onSpawnChange(SpawnChangeEvent evt) { + DynmapWorld w = new BukkitWorld(evt.getWorld()); + core.listenerManager.processWorldEvent(EventType.WORLD_SPAWN_CHANGE, w); + } + }, DynmapPlugin.this); + */ + break; + + case PLAYER_JOIN: + case PLAYER_QUIT: + /* Already handled */ + break; + + case PLAYER_BED_LEAVE: + /*TODO + pm.registerEvents(new Listener() { + @EventHandler(priority=EventPriority.MONITOR) + public void onPlayerBedLeave(PlayerBedLeaveEvent evt) { + DynmapPlayer p = new BukkitPlayer(evt.getPlayer()); + core.listenerManager.processPlayerEvent(EventType.PLAYER_BED_LEAVE, p); + } + }, DynmapPlugin.this); + */ + break; + + case PLAYER_CHAT: + if (chathandler == null) { + chathandler = new ChatHandler(); + MinecraftForge.EVENT_BUS.register(chathandler); + } + break; + + case BLOCK_BREAK: + /*TODO + pm.registerEvents(new Listener() { + @EventHandler(priority=EventPriority.MONITOR) + public void onBlockBreak(BlockBreakEvent evt) { + if(evt.isCancelled()) return; + Block b = evt.getBlock(); + if(b == null) return; + Location l = b.getLocation(); + core.listenerManager.processBlockEvent(EventType.BLOCK_BREAK, b.getType().getId(), + BukkitWorld.normalizeWorldName(l.getWorld().getName()), l.getBlockX(), l.getBlockY(), l.getBlockZ()); + } + }, DynmapPlugin.this); + */ + break; + + case SIGN_CHANGE: + /*TODO + pm.registerEvents(new Listener() { + @EventHandler(priority=EventPriority.MONITOR) + public void onSignChange(SignChangeEvent evt) { + if(evt.isCancelled()) return; + Block b = evt.getBlock(); + Location l = b.getLocation(); + String[] lines = evt.getLines(); + DynmapPlayer dp = null; + Player p = evt.getPlayer(); + if(p != null) dp = new BukkitPlayer(p); + core.listenerManager.processSignChangeEvent(EventType.SIGN_CHANGE, b.getType().getId(), + BukkitWorld.normalizeWorldName(l.getWorld().getName()), l.getBlockX(), l.getBlockY(), l.getBlockZ(), lines, dp); + } + }, DynmapPlugin.this); + */ + break; + + default: + Log.severe("Unhandled event type: " + type); + return false; + } + + registered.add(type); + return true; + } + @Override + public boolean sendWebChatEvent(String source, String name, String msg) + { + return DynmapCommonAPIListener.fireWebChatEvent(source, name, msg); + } + @Override + public void broadcastMessage(String msg) + { + ITextComponent component = new StringTextComponent(msg); + server.getPlayerList().func_232641_a_(component, ChatType.SYSTEM, Util.field_240973_b_); + Log.info(stripChatColor(msg)); + } + @Override + public String[] getBiomeIDs() + { + BiomeMap[] b = BiomeMap.values(); + String[] bname = new String[b.length]; + + for (int i = 0; i < bname.length; i++) + { + bname[i] = b[i].toString(); + } + + return bname; + } + @Override + public double getCacheHitRate() + { + if(sscache != null) + return sscache.getHitRate(); + return 0.0; + } + @Override + public void resetCacheStats() + { + if(sscache != null) + sscache.resetStats(); + } + @Override + public DynmapWorld getWorldByName(String wname) + { + return DynmapPlugin.this.getWorldByName(wname); + } + @Override + public DynmapPlayer getOfflinePlayer(String name) + { + /* + OfflinePlayer op = getServer().getOfflinePlayer(name); + if(op != null) { + return new BukkitPlayer(op); + } + */ + return null; + } + @Override + public Set checkPlayerPermissions(String player, Set perms) + { + net.minecraft.server.management.PlayerList scm = server.getPlayerList(); + if (scm == null) return Collections.emptySet(); + BanList bl = scm.getBannedPlayers(); + if (bl == null) return Collections.emptySet(); + if(bl.isBanned(getProfileByName(player))) { + return Collections.emptySet(); + } + Set rslt = hasOfflinePermissions(player, perms); + if (rslt == null) { + rslt = new HashSet(); + if(plugin.isOp(player)) { + rslt.addAll(perms); + } + } + return rslt; + } + @Override + public boolean checkPlayerPermission(String player, String perm) + { + net.minecraft.server.management.PlayerList scm = server.getPlayerList(); + if (scm == null) return false; + BanList bl = scm.getBannedPlayers(); + if (bl == null) return false; + if(bl.isBanned(getProfileByName(player))) { + return false; + } + return hasOfflinePermission(player, perm); + } + /** + * Render processor helper - used by code running on render threads to request chunk snapshot cache from server/sync thread + */ + @Override + public MapChunkCache createMapChunkCache(DynmapWorld w, List chunks, + boolean blockdata, boolean highesty, boolean biome, boolean rawbiome) + { + ForgeMapChunkCache c = (ForgeMapChunkCache) w.getChunkCache(chunks); + if(c == null) { + return null; + } + if (w.visibility_limits != null) + { + for (VisibilityLimit limit: w.visibility_limits) + { + c.setVisibleRange(limit); + } + + c.setHiddenFillStyle(w.hiddenchunkstyle); + } + + if (w.hidden_limits != null) + { + for (VisibilityLimit limit: w.hidden_limits) + { + c.setHiddenRange(limit); + } + + c.setHiddenFillStyle(w.hiddenchunkstyle); + } + + if (c.setChunkDataTypes(blockdata, biome, highesty, rawbiome) == false) + { + Log.severe("CraftBukkit build does not support biome APIs"); + } + + if (chunks.size() == 0) /* No chunks to get? */ + { + c.loadChunks(0); + return c; + } + + //Now handle any chunks in server thread that are already loaded (on server thread) + final ForgeMapChunkCache cc = c; + Future f = this.callSyncMethod(new Callable() { + public Boolean call() throws Exception { + // Update busy state on world + ForgeWorld fw = (ForgeWorld)cc.getWorld(); + //TODO + //setBusy(fw.getWorld()); + cc.getLoadedChunks(); + return true; + } + }, 0); + try { + f.get(); + } + catch (CancellationException cx) { + return null; + } + catch (ExecutionException xx) { + Log.severe("Exception while loading chunks", xx.getCause()); + return null; + } + catch (Exception ix) { + Log.severe(ix); + return null; + } + if(w.isLoaded() == false) { + return null; + } + // Now, do rest of chunk reading from calling thread + c.readChunks(chunks.size()); + + return c; + } + @Override + public int getMaxPlayers() + { + return server.getMaxPlayers(); + } + @Override + public int getCurrentPlayers() + { + return server.getPlayerList().getCurrentPlayerCount(); + } + + @SubscribeEvent + public void tickEvent(TickEvent.ServerTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + return; + } + cur_tick_starttime = System.nanoTime(); + long elapsed = cur_tick_starttime - lasttick; + lasttick = cur_tick_starttime; + avgticklen = ((avgticklen * 99) / 100) + (elapsed / 100); + tps = (double)1E9 / (double)avgticklen; + // Tick core + if (core != null) { + core.serverTick(tps); + } + + boolean done = false; + TaskRecord tr = null; + + while(!blockupdatequeue.isEmpty()) { + BlockUpdateRec r = blockupdatequeue.remove(); + BlockState bs = r.w.getBlockState(new BlockPos(r.x, r.y, r.z)); + int idx = Block.BLOCK_STATE_IDS.getId(bs); + if(!org.dynmap.hdmap.HDBlockModels.isChangeIgnoredBlock(stateByID[idx])) { + if(onblockchange_with_id) + mapManager.touch(r.wid, r.x, r.y, r.z, "blockchange[" + idx + "]"); + else + mapManager.touch(r.wid, r.x, r.y, r.z, "blockchange"); + } + } + + long now; + + synchronized(schedlock) { + cur_tick++; + now = System.nanoTime(); + tr = runqueue.peek(); + /* Nothing due to run */ + if((tr == null) || (tr.ticktorun > cur_tick) || ((now - cur_tick_starttime) > perTickLimit)) { + done = true; + } + else { + tr = runqueue.poll(); + } + } + while (!done) { + tr.future.run(); + + synchronized(schedlock) { + tr = runqueue.peek(); + now = System.nanoTime(); + /* Nothing due to run */ + if((tr == null) || (tr.ticktorun > cur_tick) || ((now - cur_tick_starttime) > perTickLimit)) { + done = true; + } + else { + tr = runqueue.poll(); + } + } + } + while(!msgqueue.isEmpty()) { + ChatMessage cm = msgqueue.poll(); + DynmapPlayer dp = null; + if(cm.sender != null) + dp = getOrAddPlayer(cm.sender); + else + dp = new ForgePlayer(null); + + core.listenerManager.processChatEvent(EventType.PLAYER_CHAT, dp, cm.message); + } + // Check for generated chunks + if((cur_tick % 20) == 0) { + } + } + + @Override + public boolean isModLoaded(String name) { + boolean loaded = ModList.get().isLoaded(name); + if (loaded) { + modsused.add(name); + } + return loaded; + } + @Override + public String getModVersion(String name) { + Optional mod = ModList.get().getModContainerById(name); // Try case sensitive lookup + if (mod.isPresent()) { + ArtifactVersion vi = mod.get().getModInfo().getVersion(); + return vi.getMajorVersion() + "." + vi.getMinorVersion() + "." + vi.getIncrementalVersion(); + } + return null; + } + @Override + public double getServerTPS() { + return tps; + } + + @Override + public String getServerIP() { + if (server.isSinglePlayer()) + return "0.0.0.0"; + else + return server.getServerHostname(); + } + @Override + public File getModContainerFile(String name) { + ModFileInfo mfi = ModList.get().getModFileById(name); // Try case sensitive lookup + if (mfi != null) { + File f = mfi.getFile().getFilePath().toFile(); + return f; + } + return null; + } + @Override + public List getModList() { + List mil = ModList.get().getMods(); + List lst = new ArrayList(); + for (ModInfo mi : mil) { + lst.add(mi.getModId()); + } + return lst; + } + + @Override + public Map getBlockIDMap() { + Map map = new HashMap(); + return map; + } + + @Override + public InputStream openResource(String modid, String rname) { + if (modid == null) modid = "minecraft"; + + Optional mc = ModList.get().getModContainerById(modid); + Object mod = (mc.isPresent()) ? mc.get().getMod() : null; + if (mod != null) { + ClassLoader cl = mod.getClass().getClassLoader(); + if (cl == null) cl = ClassLoader.getSystemClassLoader(); + InputStream is = cl.getResourceAsStream(rname); + if (is != null) { + return is; + } + } + List mcl = ModList.get().getMods(); + for (ModInfo mci : mcl) { + mc = ModList.get().getModContainerById(mci.getModId()); + mod = (mc.isPresent()) ? mc.get().getMod() : null; + if (mod == null) continue; + ClassLoader cl = mod.getClass().getClassLoader(); + if (cl == null) cl = ClassLoader.getSystemClassLoader(); + InputStream is = cl.getResourceAsStream(rname); + if (is != null) { + return is; + } + } + return null; + } + /** + * Get block unique ID map (module:blockid) + */ + @Override + public Map getBlockUniqueIDMap() { + HashMap map = new HashMap(); + return map; + } + /** + * Get item unique ID map (module:itemid) + */ + @Override + public Map getItemUniqueIDMap() { + HashMap map = new HashMap(); + return map; + } + + } + private static final Gson gson = new GsonBuilder().create(); + + public class TexturesPayload { + public long timestamp; + public String profileId; + public String profileName; + public boolean isPublic; + public Map textures; + + } + public class ProfileTexture { + public String url; + } + + /** + * Player access abstraction class + */ + public class ForgePlayer extends ForgeCommandSender implements DynmapPlayer + { + private PlayerEntity player; + private final String skinurl; + private final UUID uuid; + + + public ForgePlayer(PlayerEntity p) + { + player = p; + String url = null; + if (player != null) { + uuid = player.getUniqueID(); + GameProfile prof = player.getGameProfile(); + if (prof != null) { + Property textureProperty = Iterables.getFirst(prof.getProperties().get("textures"), null); + + if (textureProperty != null) { + TexturesPayload result = null; + try { + String json = new String(Base64.decodeBase64(textureProperty.getValue()), Charsets.UTF_8); + result = gson.fromJson(json, TexturesPayload.class); + } catch (JsonParseException e) { + } + if ((result != null) && (result.textures != null) && (result.textures.containsKey("SKIN"))) { + url = result.textures.get("SKIN").url; + } + } + } + } + else { + uuid = null; + } + skinurl = url; + } + @Override + public boolean isConnected() + { + return true; + } + @Override + public String getName() + { + if(player != null) { + String n = player.getEntity().getName().getString();; + return n; + } + else + return "[Server]"; + } + @Override + public String getDisplayName() + { + if(player != null) { + String n = player.getDisplayName().getString(); + return n; + } + else + return "[Server]"; + } + @Override + public boolean isOnline() + { + return true; + } + @Override + public DynmapLocation getLocation() + { + if (player == null) { + return null; + } + Vector3d v = player.getPositionVec(); + return toLoc(player.world, v.x, v.y, v.z); + } + @Override + public String getWorld() + { + if (player == null) + { + return null; + } + + if (player.world != null) + { + return DynmapPlugin.this.getWorld((IServerWorld)player.world).getName(); + } + + return null; + } + @Override + public InetSocketAddress getAddress() + { + if((player != null) && (player instanceof ServerPlayerEntity)) { + ServerPlayNetHandler nsh = ((ServerPlayerEntity)player).connection; + if((nsh != null) && (getNetworkManager(nsh) != null)) { + SocketAddress sa = getNetworkManager(nsh).getRemoteAddress(); + if(sa instanceof InetSocketAddress) { + return (InetSocketAddress)sa; + } + } + } + return null; + } + @Override + public boolean isSneaking() + { + if (player != null) + { + return player.isSneaking(); + } + + return false; + } + @Override + public double getHealth() + { + if (player != null) + { + double h = player.getHealth(); + if(h > 20) h = 20; + return h; // Scale to 20 range + } + else + { + return 0; + } + } + @Override + public int getArmorPoints() + { + if (player != null) + { + return player.getTotalArmorValue(); + } + else + { + return 0; + } + } + @Override + public DynmapLocation getBedSpawnLocation() + { + return null; + } + @Override + public long getLastLoginTime() + { + return 0; + } + @Override + public long getFirstLoginTime() + { + return 0; + } + @Override + public boolean hasPrivilege(String privid) + { + if(player != null) + return hasPerm(player, privid); + return false; + } + @Override + public boolean isOp() + { + return DynmapPlugin.this.isOp(player.getEntity().getName().getString()); + } + @Override + public void sendMessage(String msg) + { + ITextComponent ichatcomponent = new StringTextComponent(msg); + server.getPlayerList().func_232641_a_(ichatcomponent, ChatType.CHAT, player.getUniqueID()); + } + @Override + public boolean isInvisible() { + if(player != null) { + return player.isInvisible(); + } + return false; + } + @Override + public int getSortWeight() { + Integer wt = sortWeights.get(getName()); + if (wt != null) + return wt; + return 0; + } + @Override + public void setSortWeight(int wt) { + if (wt == 0) { + sortWeights.remove(getName()); + } + else { + sortWeights.put(getName(), wt); + } + } + @Override + public boolean hasPermissionNode(String node) { + if(player != null) + return hasPermNode(player, node); + return false; + } + @Override + public String getSkinURL() { + return skinurl; + } + @Override + public UUID getUUID() { + return uuid; + } + /** + * Send title and subtitle text (called from server thread) + */ + @Override + public void sendTitleText(String title, String subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) { + if (player instanceof ServerPlayerEntity) { + ServerPlayerEntity mp = (ServerPlayerEntity) player; + STitlePacket times = new STitlePacket(fadeInTicks, stayTicks, fadeOutTicks); + mp.connection.sendPacket(times); + if (title != null) { + STitlePacket titlepkt = new STitlePacket(STitlePacket.Type.TITLE, new StringTextComponent(title)); + mp.connection.sendPacket(titlepkt); + } + + if (subtitle != null) { + STitlePacket subtitlepkt = new STitlePacket(STitlePacket.Type.SUBTITLE, new StringTextComponent(subtitle)); + mp.connection.sendPacket(subtitlepkt); + } + } + } + } + /* Handler for generic console command sender */ + public class ForgeCommandSender implements DynmapCommandSender + { + private CommandSource sender; + + protected ForgeCommandSender() { + sender = null; + } + + public ForgeCommandSender(CommandSource send) + { + sender = send; + } + + @Override + public boolean hasPrivilege(String privid) + { + return true; + } + + @Override + public void sendMessage(String msg) + { + if(sender != null) { + ITextComponent ichatcomponent = new StringTextComponent(msg); + sender.sendFeedback(ichatcomponent, false); + } + } + + @Override + public boolean isConnected() + { + return false; + } + @Override + public boolean isOp() + { + return true; + } + @Override + public boolean hasPermissionNode(String node) { + return true; + } + } + + public void loadExtraBiomes(String mcver) { + int cnt = 0; + BiomeMap.loadWellKnownByVersion(mcver); + + Biome[] list = getBiomeList(); + + for(int i = 0; i < list.length; i++) { + Biome bb = list[i]; + if(bb != null) { + String id = bb.toString(); + float tmp = bb.func_242445_k(), hum = bb.getDownfall(); + int watermult = bb.func_235089_q_().field_235206_c_; + Log.verboseinfo("biome[" + i + "]: hum=" + hum + ", tmp=" + tmp + ", mult=" + Integer.toHexString(watermult)); + + BiomeMap bmap = BiomeMap.byBiomeID(i); + if (bmap.isDefault()) { + bmap = new BiomeMap(i, id, tmp, hum); + Log.verboseinfo("Add custom biome [" + bmap.toString() + "] (" + i + ")"); + cnt++; + } + else { + bmap.setTemperature(tmp); + bmap.setRainfall(hum); + } + if (watermult != -1) { + bmap.setWaterColorMultiplier(watermult); + Log.verboseinfo("Set watercolormult for " + bmap.toString() + " (" + i + ") to " + Integer.toHexString(watermult)); + } + } + } + if(cnt > 0) + Log.info("Added " + cnt + " custom biome mappings"); + } + + private String[] getBiomeNames() { + Biome[] list = getBiomeList(); + String[] lst = new String[list.length]; + for(int i = 0; i < list.length; i++) { + Biome bb = list[i]; + if (bb != null) { + lst[i] = bb.toString(); + } + } + return lst; + } + + public void onEnable() + { + /* Get MC version */ + String mcver = server.getMinecraftVersion(); + + /* Load extra biomes */ + loadExtraBiomes(mcver); + /* Set up player login/quit event handler */ + registerPlayerLoginListener(); + + /* Initialize permissions handler */ + permissions = FilePermissions.create(); + if(permissions == null) { + permissions = new OpPermissions(new String[] { "webchat", "marker.icons", "marker.list", "webregister", "stats", "hide.self", "show.self" }); + } + /* Get and initialize data folder */ + File dataDirectory = new File("dynmap"); + + if (dataDirectory.exists() == false) + { + dataDirectory.mkdirs(); + } + + /* Instantiate core */ + if (core == null) + { + core = new DynmapCore(); + } + + /* Inject dependencies */ + core.setPluginJarFile(DynmapMod.jarfile); + core.setPluginVersion(DynmapMod.ver); + core.setMinecraftVersion(mcver); + core.setDataFolder(dataDirectory); + core.setServer(fserver); + ForgeMapChunkCache.init(); + core.setTriggerDefault(TRIGGER_DEFAULTS); + core.setBiomeNames(getBiomeNames()); + + if(!core.initConfiguration(null)) + { + return; + } + // Extract default permission example, if needed + File filepermexample = new File(core.getDataFolder(), "permissions.yml.example"); + core.createDefaultFileFromResource("/permissions.yml.example", filepermexample); + + DynmapCommonAPIListener.apiInitialized(core); + } + + private static int test(CommandSource source) throws CommandSyntaxException + { + System.out.println(source.toString()); + return 1; + } + + private DynmapCommand dynmapCmd; + private DmapCommand dmapCmd; + private DmarkerCommand dmarkerCmd; + private DynmapExpCommand dynmapexpCmd; + + public void onStarting(CommandDispatcher cd) { + /* Register command hander */ + dynmapCmd = new DynmapCommand(this); + dmapCmd = new DmapCommand(this); + dmarkerCmd = new DmarkerCommand(this); + dynmapexpCmd = new DynmapExpCommand(this); + dynmapCmd.register(cd); + dmapCmd.register(cd); + dmarkerCmd.register(cd); + dynmapexpCmd.register(cd); + + Log.info("Register commands"); + } + + public void onStart() { + initializeBlockStates(); + /* Enable core */ + if (!core.enableCore(null)) + { + return; + } + core_enabled = true; + VersionCheck.runCheck(core); + // Get per tick time limit + perTickLimit = core.getMaxTickUseMS() * 1000000; + // Prep TPS + lasttick = System.nanoTime(); + tps = 20.0; + + /* Register tick handler */ + if(!tickregistered) { + MinecraftForge.EVENT_BUS.register(fserver); + tickregistered = true; + } + + playerList = core.playerList; + sscache = new SnapshotCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache()); + /* Get map manager from core */ + mapManager = core.getMapManager(); + + /* Load saved world definitions */ + loadWorlds(); + + /* Initialized the currently loaded worlds */ + if(server.getWorlds() != null) { + for (ServerWorld world : server.getWorlds()) { + ForgeWorld w = this.getWorld(world); + /*NOTYET - need rest of forge + if(DimensionManager.getWorld(world.provider.getDimensionId()) == null) { // If not loaded + w.setWorldUnloaded(); + } + */ + } + } + for(ForgeWorld w : worlds.values()) { + if (core.processWorldLoad(w)) { /* Have core process load first - fire event listeners if good load after */ + if(w.isLoaded()) { + core.listenerManager.processWorldEvent(EventType.WORLD_LOAD, w); + } + } + } + core.updateConfigHashcode(); + + /* Register our update trigger events */ + registerEvents(); + Log.info("Register events"); + + //DynmapCommonAPIListener.apiInitialized(core); + + Log.info("Enabled"); + } + + public void onDisable() + { + DynmapCommonAPIListener.apiTerminated(); + + //if (metrics != null) { + // metrics.stop(); + // metrics = null; + //} + /* Save worlds */ + saveWorlds(); + + /* Purge tick queue */ + fserver.runqueue.clear(); + + /* Disable core */ + core.disableCore(); + core_enabled = false; + + if (sscache != null) + { + sscache.cleanup(); + sscache = null; + } + + Log.info("Disabled"); + } + + void onCommand(CommandSource sender, String cmd, String[] args) + { + DynmapCommandSender dsender; + PlayerEntity psender; + try { + psender = sender.asPlayer(); + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException x) { + psender = null; + } + + if (psender != null) + { + dsender = new ForgePlayer(psender); + } + else + { + dsender = new ForgeCommandSender(sender); + } + + core.processCommand(dsender, cmd, cmd, args); + } + + private DynmapLocation toLoc(World worldObj, double x, double y, double z) + { + return new DynmapLocation(DynmapPlugin.this.getWorld((IServerWorld)worldObj).getName(), x, y, z); + } + + public class PlayerTracker { + @SubscribeEvent + public void onPlayerLogin(PlayerLoggedInEvent event) { + if(!core_enabled) return; + final DynmapPlayer dp = getOrAddPlayer(event.getPlayer()); + /* This event can be called from off server thread, so push processing there */ + core.getServer().scheduleServerTask(new Runnable() { + public void run() { + core.listenerManager.processPlayerEvent(EventType.PLAYER_JOIN, dp); + } + }, 2); + } + @SubscribeEvent + public void onPlayerLogout(PlayerLoggedOutEvent event) { + if(!core_enabled) return; + final DynmapPlayer dp = getOrAddPlayer(event.getPlayer()); + final String name = event.getPlayer().getEntity().getName().getString(); + /* This event can be called from off server thread, so push processing there */ + core.getServer().scheduleServerTask(new Runnable() { + public void run() { + core.listenerManager.processPlayerEvent(EventType.PLAYER_QUIT, dp); + players.remove(name); + } + }, 0); + } + @SubscribeEvent + public void onPlayerChangedDimension(PlayerChangedDimensionEvent event) { + if(!core_enabled) return; + getOrAddPlayer(event.getPlayer()); // Freshen player object reference + } + @SubscribeEvent + public void onPlayerRespawn(PlayerRespawnEvent event) { + if(!core_enabled) return; + getOrAddPlayer(event.getPlayer()); // Freshen player object reference + } + } + private PlayerTracker playerTracker = null; + + private void registerPlayerLoginListener() + { + if (playerTracker == null) { + playerTracker = new PlayerTracker(); + MinecraftForge.EVENT_BUS.register(playerTracker); + } + } + + public class WorldTracker { + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleWorldLoad(WorldEvent.Load event) { + if(!core_enabled) return; + IWorld w = event.getWorld(); + if(!(w instanceof ServerWorld)) return; + final ForgeWorld fw = getWorld((IServerWorld)w); + // This event can be called from off server thread, so push processing there + core.getServer().scheduleServerTask(new Runnable() { + public void run() { + if(core.processWorldLoad(fw)) // Have core process load first - fire event listeners if good load after + core.listenerManager.processWorldEvent(EventType.WORLD_LOAD, fw); + } + }, 0); + } + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleWorldUnload(WorldEvent.Unload event) { + if(!core_enabled) return; + IWorld w = event.getWorld(); + if(!(w instanceof ServerWorld)) return; + final ForgeWorld fw = getWorld((IServerWorld)w); + if(fw != null) { + // This event can be called from off server thread, so push processing there + core.getServer().scheduleServerTask(new Runnable() { + public void run() { + core.listenerManager.processWorldEvent(EventType.WORLD_UNLOAD, fw); + core.processWorldUnload(fw); + } + }, 0); + // Set world unloaded (needs to be immediate, since it may be invalid after event) + fw.setWorldUnloaded(); + // Clean up tracker + //WorldUpdateTracker wut = updateTrackers.remove(fw.getName()); + //if(wut != null) wut.world = null; + } + } + + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleChunkLoad(ChunkEvent.Load event) { + if(!onchunkgenerate) return; + + IWorld w = event.getWorld(); + if(!(w instanceof ServerWorld)) return; + IChunk c = event.getChunk(); + if ((c != null) && (c.getStatus() == ChunkStatus.FULL)) { + ForgeWorld fw = getWorld((IServerWorld)w, false); + if (fw != null) { + addKnownChunk(fw, c.getPos()); + } + } + } + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleChunkUnload(ChunkEvent.Unload event) { + if(!onchunkgenerate) return; + + IWorld w = event.getWorld(); + if(!(w instanceof ServerWorld)) return; + IChunk c = event.getChunk(); + if ((c != null) && (c.getStatus() == ChunkStatus.FULL)) { + ForgeWorld fw = getWorld((IServerWorld)w, false); + ChunkPos cp = c.getPos(); + if (fw != null) { + if (!checkIfKnownChunk(fw, cp)) { + int ymax = 0; + ChunkSection[] sections = c.getSections(); + for(int i = 0; i < sections.length; i++) { + if((sections[i] != null) && (sections[i].isEmpty() == false)) { + ymax = 16*(i+1); + } + } + int x = cp.x << 4; + int z = cp.z << 4; + // If not empty AND not initial scan + if (ymax > 0) { + Log.info("New generated chunk detected at " + cp + " for " + fw.getName()); + mapManager.touchVolume(fw.getName(), x, 0, z, x+15, ymax, z+16, "chunkgenerate"); + } + } + removeKnownChunk(fw, cp); + } + } + } + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleChunkDataSave(ChunkDataEvent.Save event) { + if(!onchunkgenerate) return; + + IWorld w = event.getWorld(); + if(!(w instanceof ServerWorld)) return; + IChunk c = event.getChunk(); + if ((c != null) && (c.getStatus() == ChunkStatus.FULL)) { + ForgeWorld fw = getWorld((IServerWorld)w, false); + ChunkPos cp = c.getPos(); + if (fw != null) { + if (!checkIfKnownChunk(fw, cp)) { + int ymax = 0; + ChunkSection[] sections = c.getSections(); + for(int i = 0; i < sections.length; i++) { + if((sections[i] != null) && (sections[i].isEmpty() == false)) { + ymax = 16*(i+1); + } + } + int x = cp.x << 4; + int z = cp.z << 4; + // If not empty AND not initial scan + if (ymax > 0) { + mapManager.touchVolume(fw.getName(), x, 0, z, x+15, ymax, z+16, "chunkgenerate"); + } + addKnownChunk(fw, cp); + } + } + } + } + @SubscribeEvent(priority=EventPriority.LOWEST) + public void handleBlockEvent(BlockEvent event) { + if(!core_enabled) return; + if(!onblockchange) return; + BlockUpdateRec r = new BlockUpdateRec(); + r.w = event.getWorld(); + ForgeWorld fw = getWorld((IServerWorld)r.w, false); + if (fw == null) return; + r.wid = fw.getName(); + BlockPos p = event.getPos(); + r.x = p.getX(); + r.y = p.getY(); + r.z = p.getZ(); + blockupdatequeue.add(r); + } + } + private WorldTracker worldTracker = null; + private boolean onblockchange = false; + private boolean onchunkpopulate = false; + private boolean onchunkgenerate = false; + private boolean onblockchange_with_id = false; + + private void registerEvents() + { + // To trigger rendering. + onblockchange = core.isTrigger("blockupdate"); + onchunkpopulate = core.isTrigger("chunkpopulate"); + onchunkgenerate = core.isTrigger("chunkgenerate"); + onblockchange_with_id = core.isTrigger("blockupdate-with-id"); + if(onblockchange_with_id) + onblockchange = true; + if ((worldTracker == null) && (onblockchange || onchunkpopulate || onchunkgenerate)) { + worldTracker = new WorldTracker(); + MinecraftForge.EVENT_BUS.register(worldTracker); + } + // Prime the known full chunks + if (onchunkgenerate && (server.getWorlds() != null)) { + for (ServerWorld world : server.getWorlds()) { + ForgeWorld fw = getWorld(world); + if (fw == null) continue; + Long2ObjectLinkedOpenHashMap chunks = world.getChunkProvider().chunkManager.immutableLoadedChunks; + for (Entry k : chunks.long2ObjectEntrySet()) { + long key = k.getKey().longValue(); + ChunkHolder ch = k.getValue(); + IChunk c = null; + try { + c = ch.func_219302_f().getNow(null); + } catch (Exception x) { } + if (c == null) continue; + ChunkStatus cs = c.getStatus(); + ChunkPos pos = ch.getPosition(); + if (cs == ChunkStatus.FULL) { // Cooked? + // Add it as known + addKnownChunk(fw, pos); + } + } + } + } + } + + private ForgeWorld getWorldByName(String name) { + return worlds.get(name); + } + + private ForgeWorld getWorld(IServerWorld w) { + return getWorld(w, true); + } + + private ForgeWorld getWorld(IServerWorld w, boolean add_if_not_found) { + if(last_world == w) { + return last_fworld; + } + String wname = ForgeWorld.getWorldName(w); + + for(ForgeWorld fw : worlds.values()) { + if(fw.getRawName().equals(wname)) { + last_world = w; + last_fworld = fw; + if(fw.isLoaded() == false) { + fw.setWorldLoaded(w); + } + return fw; + } + } + ForgeWorld fw = null; + if(add_if_not_found) { + /* Add to list if not found */ + fw = new ForgeWorld(w); + worlds.put(fw.getName(), fw); + } + last_world = w; + last_fworld = fw; + return fw; + } + + private void saveWorlds() { + File f = new File(core.getDataFolder(), "forgeworlds.yml"); + ConfigurationNode cn = new ConfigurationNode(f); + ArrayList> lst = new ArrayList>(); + for(DynmapWorld fw : core.mapManager.getWorlds()) { + HashMap vals = new HashMap(); + vals.put("name", fw.getRawName()); + vals.put("height", fw.worldheight); + vals.put("sealevel", fw.sealevel); + vals.put("nether", fw.isNether()); + vals.put("the_end", ((ForgeWorld)fw).isTheEnd()); + vals.put("title", fw.getTitle()); + lst.add(vals); + } + cn.put("worlds", lst); + cn.put("useSaveFolderAsName", useSaveFolder); + cn.put("maxWorldHeight", ForgeWorld.getMaxWorldHeight()); + + cn.save(); + } + private void loadWorlds() { + File f = new File(core.getDataFolder(), "forgeworlds.yml"); + if(f.canRead() == false) { + useSaveFolder = true; + return; + } + ConfigurationNode cn = new ConfigurationNode(f); + cn.load(); + // If defined, use maxWorldHeight + ForgeWorld.setMaxWorldHeight(cn.getInteger("maxWorldHeight", 256)); + + // If setting defined, use it + if (cn.containsKey("useSaveFolderAsName")) { + useSaveFolder = cn.getBoolean("useSaveFolderAsName", useSaveFolder); + } + List> lst = cn.getMapList("worlds"); + if(lst == null) { + Log.warning("Discarding bad forgeworlds.yml"); + return; + } + + for(Map world : lst) { + try { + String name = (String)world.get("name"); + int height = (Integer)world.get("height"); + int sealevel = (Integer)world.get("sealevel"); + boolean nether = (Boolean)world.get("nether"); + boolean theend = (Boolean)world.get("the_end"); + String title = (String)world.get("title"); + if(name != null) { + ForgeWorld fw = new ForgeWorld(name, height, sealevel, nether, theend, title); + fw.setWorldUnloaded(); + core.processWorldLoad(fw); + worlds.put(fw.getName(), fw); + } + } catch (Exception x) { + Log.warning("Unable to load saved worlds from forgeworlds.yml"); + return; + } + } + } + public void serverStarted() { + this.onStart(); + if (core != null) { + core.serverStarted(); + } + } + public MinecraftServer getMCServer() { + return server; + } +} + +class DynmapCommandHandler +{ + private String cmd; + private DynmapPlugin plugin; + + public DynmapCommandHandler(String cmd, DynmapPlugin p) + { + this.cmd = cmd; + this.plugin = p; + } + + public void register(CommandDispatcher cd) { + cd.register(Commands.literal(cmd). + then(RequiredArgumentBuilder. argument("args", StringArgumentType.greedyString()). + executes((ctx) -> this.execute(plugin.getMCServer(), ctx.getSource(), ctx.getInput()))). + executes((ctx) -> this.execute(plugin.getMCServer(), ctx.getSource(), ctx.getInput()))); + } + +// @Override + public int execute(MinecraftServer server, CommandSource sender, + String cmdline) throws CommandException { + String[] args = cmdline.split("\\s+"); + plugin.onCommand(sender, cmd, Arrays.copyOfRange(args, 1, args.length)); + return 1; + } + +// @Override + public String getUsage(CommandSource arg0) { + return "Run /" + cmd + " help for details on using command"; + } +} + +class DynmapCommand extends DynmapCommandHandler { + DynmapCommand(DynmapPlugin p) { + super("dynmap", p); + } +} +class DmapCommand extends DynmapCommandHandler { + DmapCommand(DynmapPlugin p) { + super("dmap", p); + } +} +class DmarkerCommand extends DynmapCommandHandler { + DmarkerCommand(DynmapPlugin p) { + super("dmarker", p); + } +} +class DynmapExpCommand extends DynmapCommandHandler { + DynmapExpCommand(DynmapPlugin p) { + super("dynmapexp", p); + } +} + diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeMapChunkCache.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeMapChunkCache.java new file mode 100644 index 00000000..f069480e --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeMapChunkCache.java @@ -0,0 +1,1489 @@ +package org.dynmap.forge_1_16_2; + +import java.io.DataInputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import net.minecraft.nbt.ByteArrayNBT; +import net.minecraft.nbt.ByteNBT; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.DoubleNBT; +import net.minecraft.nbt.FloatNBT; +import net.minecraft.nbt.INBT; +import net.minecraft.nbt.IntArrayNBT; +import net.minecraft.nbt.IntNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.LongNBT; +import net.minecraft.nbt.ShortNBT; +import net.minecraft.nbt.StringNBT; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.AbstractChunkProvider; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.storage.ChunkSerializer; +import net.minecraft.world.chunk.storage.RegionFileCache; +import net.minecraft.world.server.ChunkManager; +import net.minecraft.world.server.ServerChunkProvider; +import net.minecraft.world.server.ServerWorld; + +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapCore; +import org.dynmap.DynmapWorld; +import org.dynmap.Log; +import org.dynmap.common.BiomeMap; +import org.dynmap.forge_1_16_2.SnapshotCache.SnapshotRec; +import org.dynmap.hdmap.HDBlockModels; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.renderer.RenderPatchFactory; +import org.dynmap.utils.DynIntHashMap; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.MapIterator; +import org.dynmap.utils.BlockStep; +import org.dynmap.utils.VisibilityLimit; + +/** + * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread + */ +public class ForgeMapChunkCache extends MapChunkCache +{ + private static boolean init = false; + private static Field updateEntityTick = null; + /* ChunkManager fields */ + private static Field chunksToRemove = null; // Map + + /* ChunjManager Pending fields */ + private static Field chunkCoord = null; + private static Field nbtTag = null; + + private World w; + private DynmapWorld dw; + private ServerChunkProvider cps; + private int nsect; + private List chunks; + private ListIterator iterator; + private int x_min, x_max, z_min, z_max; + private int x_dim; + private boolean biome, biomeraw, highesty, blockdata; + private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR; + private List visible_limits = null; + private List hidden_limits = null; + private boolean isempty = true; + private int snapcnt; + private ChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */ + private DynIntHashMap[] snaptile; + private byte[][] sameneighborbiomecnt; + private BiomeMap[][] biomemap; + private boolean[][] isSectionNotEmpty; /* Indexed by snapshot index, then by section index */ + + + private static final BlockStep unstep[] = { BlockStep.X_MINUS, BlockStep.Y_MINUS, BlockStep.Z_MINUS, + BlockStep.X_PLUS, BlockStep.Y_PLUS, BlockStep.Z_PLUS + }; + + private static BiomeMap[] biome_to_bmap; + + private static final int getIndexInChunk(int cx, int cy, int cz) { + return (cy << 8) | (cz << 4) | cx; + } + + /** + * Iterator for traversing map chunk cache (base is for non-snapshot) + */ + public class OurMapIterator implements MapIterator + { + private int x, y, z, chunkindex, bx, bz; + private ChunkSnapshot snap; + private BlockStep laststep; + private DynmapBlockState blk; + private final int worldheight; + private final int x_base; + private final int z_base; + + OurMapIterator(int x0, int y0, int z0) + { + x_base = x_min << 4; + z_base = z_min << 4; + + if (biome) + { + biomePrep(); + } + + initialize(x0, y0, z0); + worldheight = w.getHeight(); + } + @Override + public final void initialize(int x0, int y0, int z0) + { + this.x = x0; + this.y = y0; + this.z = z0; + this.chunkindex = ((x >> 4) - x_min) + (((z >> 4) - z_min) * x_dim); + this.bx = x & 0xF; + this.bz = z & 0xF; + + if((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = EMPTY; + } + else { + snap = snaparray[chunkindex]; + } + + laststep = BlockStep.Y_MINUS; + + if ((y >= 0) && (y < worldheight)) + { + blk = null; + } + else + { + blk = DynmapBlockState.AIR; + } + } + @Override + public int getBlockSkyLight() + { + try + { + return snap.getBlockSkyLight(bx, y, bz); + } + catch (ArrayIndexOutOfBoundsException aioobx) + { + return 15; + } + } + @Override + public final int getBlockEmittedLight() + { + try + { + return snap.getBlockEmittedLight(bx, y, bz); + } + catch (ArrayIndexOutOfBoundsException aioobx) + { + return 0; + } + } + private void biomePrep() + { + if (sameneighborbiomecnt != null) + { + return; + } + + int x_size = x_dim << 4; + int z_size = (z_max - z_min + 1) << 4; + sameneighborbiomecnt = new byte[x_size][]; + biomemap = new BiomeMap[x_size][]; + + for (int i = 0; i < x_size; i++) + { + sameneighborbiomecnt[i] = new byte[z_size]; + biomemap[i] = new BiomeMap[z_size]; + } + + for (int i = 0; i < x_size; i++) + { + for (int j = 0; j < z_size; j++) + { + if (j == 0) + initialize(i + x_base, 64, z_base); + else + stepPosition(BlockStep.Z_PLUS); + + int bb = snap.getBiome(bx, bz); + BiomeMap bm = BiomeMap.byBiomeID(bb); + + biomemap[i][j] = bm; + int cnt = 0; + + if (i > 0) + { + if (bm == biomemap[i - 1][j]) /* Same as one to left */ + { + cnt++; + sameneighborbiomecnt[i - 1][j]++; + } + + if ((j > 0) && (bm == biomemap[i - 1][j - 1])) + { + cnt++; + sameneighborbiomecnt[i - 1][j - 1]++; + } + + if ((j < (z_size - 1)) && (bm == biomemap[i - 1][j + 1])) + { + cnt++; + sameneighborbiomecnt[i - 1][j + 1]++; + } + } + + if ((j > 0) && (biomemap[i][j] == biomemap[i][j - 1])) /* Same as one to above */ + { + cnt++; + sameneighborbiomecnt[i][j - 1]++; + } + + sameneighborbiomecnt[i][j] = (byte)cnt; + } + } + } + @Override + public final BiomeMap getBiome() + { + try + { + return biomemap[x - x_base][z - z_base]; + } + catch (Exception ex) + { + return BiomeMap.NULL; + } + } + @Override + public final int getSmoothGrassColorMultiplier(int[] colormap) + { + int mult = 0xFFFFFF; + + try + { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte)8) /* All neighbors same? */ + { + mult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); + } + else + { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) + { + for (int zoff = -1; zoff < 2; zoff++) + { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } + catch (Exception x) + { + mult = 0xFFFFFF; + } + + return mult; + } + @Override + public final int getSmoothFoliageColorMultiplier(int[] colormap) + { + int mult = 0xFFFFFF; + + try + { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte)8) /* All neighbors same? */ + { + mult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); + } + else + { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) + { + for (int zoff = -1; zoff < 2; zoff++) + { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } + catch (Exception x) + { + mult = 0xFFFFFF; + } + + return mult; + } + @Override + public final int getSmoothColorMultiplier(int[] colormap, int[] swampmap) + { + int mult = 0xFFFFFF; + + try + { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte)8) /* All neighbors same? */ + { + if (bm == BiomeMap.SWAMPLAND) + { + mult = swampmap[bm.biomeLookup()]; + } + else + { + mult = colormap[bm.biomeLookup()]; + } + } + else + { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) + { + for (int zoff = -1; zoff < 2; zoff++) + { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult; + + if (bm == BiomeMap.SWAMPLAND) + { + rmult = swampmap[bm.biomeLookup()]; + } + else + { + rmult = colormap[bm.biomeLookup()]; + } + + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } + catch (Exception x) + { + mult = 0xFFFFFF; + } + + return mult; + } + @Override + public final int getSmoothWaterColorMultiplier() + { + try + { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte)8) /* All neighbors same? */ + { + return bm.getWaterColorMult(); + } + + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) + { + for (int zoff = -1; zoff < 2; zoff++) + { + bm = biomemap[rx + xoff][rz + zoff]; + int mult = bm.getWaterColorMult(); + raccum += (mult >> 16) & 0xFF; + gaccum += (mult >> 8) & 0xFF; + baccum += mult & 0xFF; + } + } + + return ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + catch (Exception x) + { + return 0xFFFFFF; + } + } + @Override + public final int getSmoothWaterColorMultiplier(int[] colormap) + { + int mult = 0xFFFFFF; + + try + { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte)8) /* All neighbors same? */ + { + mult = colormap[bm.biomeLookup()]; + } + else + { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) + { + for (int zoff = -1; zoff < 2; zoff++) + { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = colormap[bm.biomeLookup()]; + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } + catch (Exception x) + { + mult = 0xFFFFFF; + } + + return mult; + } + /** + * Step current position in given direction + */ + @Override + public final void stepPosition(BlockStep step) + { + blk = null; + + switch (step.ordinal()) + { + case 0: + x++; + bx++; + + if (bx == 16) /* Next chunk? */ + { + bx = 0; + chunkindex++; + if((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = EMPTY; + } + else { + snap = snaparray[chunkindex]; + } + } + + break; + + case 1: + y++; + + if (y >= worldheight) + { + blk = DynmapBlockState.AIR; + } + + break; + + case 2: + z++; + bz++; + + if (bz == 16) /* Next chunk? */ + { + bz = 0; + chunkindex += x_dim; + if((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = EMPTY; + } + else { + snap = snaparray[chunkindex]; + } + } + break; + + case 3: + x--; + bx--; + + if (bx == -1) /* Next chunk? */ + { + bx = 15; + chunkindex--; + if((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = EMPTY; + } + else { + snap = snaparray[chunkindex]; + } + } + + break; + + case 4: + y--; + + if (y < 0) + { + blk = DynmapBlockState.AIR; + } + + break; + + case 5: + z--; + bz--; + + if (bz == -1) /* Next chunk? */ + { + bz = 15; + chunkindex -= x_dim; + if((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = EMPTY; + } + else { + snap = snaparray[chunkindex]; + } + } + break; + } + + laststep = step; + } + /** + * Unstep current position to previous position + */ + @Override + public BlockStep unstepPosition() + { + BlockStep ls = laststep; + stepPosition(unstep[ls.ordinal()]); + return ls; + } + /** + * Unstep current position in oppisite director of given step + */ + @Override + public void unstepPosition(BlockStep s) + { + stepPosition(unstep[s.ordinal()]); + } + @Override + public final void setY(int y) + { + if (y > this.y) + { + laststep = BlockStep.Y_PLUS; + } + else + { + laststep = BlockStep.Y_MINUS; + } + + this.y = y; + + if ((y < 0) || (y >= worldheight)) + { + blk = DynmapBlockState.AIR; + } + else + { + blk = null; + } + } + @Override + public final int getX() + { + return x; + } + @Override + public final int getY() + { + return y; + } + @Override + public final int getZ() + { + return z; + } + @Override + public final DynmapBlockState getBlockTypeAt(BlockStep s) + { + if (s == BlockStep.Y_MINUS) + { + if (y > 0) + { + return snap.getBlockType(bx, y - 1, bz); + } + } + else if (s == BlockStep.Y_PLUS) + { + if (y < (worldheight - 1)) + { + return snap.getBlockType(bx, y + 1, bz); + } + } + else + { + BlockStep ls = laststep; + stepPosition(s); + DynmapBlockState tid = snap.getBlockType(bx, y, bz); + unstepPosition(); + laststep = ls; + return tid; + } + + return DynmapBlockState.AIR; + } + @Override + public BlockStep getLastStep() + { + return laststep; + } + @Override + public int getWorldHeight() + { + return worldheight; + } + @Override + public long getBlockKey() + { + return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz; + } + @Override + public final boolean isEmptySection() + { + try + { + return !isSectionNotEmpty[chunkindex][y >> 4]; + } + catch (Exception x) + { + initSectionData(chunkindex); + return !isSectionNotEmpty[chunkindex][y >> 4]; + } + } + @Override + public RenderPatchFactory getPatchFactory() { + return HDBlockModels.getPatchDefinitionFactory(); + } + @Override + public Object getBlockTileEntityField(String fieldId) { + try { + int idx = getIndexInChunk(bx,y,bz); + Object[] vals = (Object[])snaptile[chunkindex].get(idx); + for (int i = 0; i < vals.length; i += 2) { + if (vals[i].equals(fieldId)) { + return vals[i+1]; + } + } + } catch (Exception x) { + } + return null; + } + @Override + public DynmapBlockState getBlockTypeAt(int xoff, int yoff, int zoff) { + int xx = this.x + xoff; + int yy = this.y + yoff; + int zz = this.z + zoff; + int idx = ((xx >> 4) - x_min) + (((zz >> 4) - z_min) * x_dim); + try { + return snaparray[idx].getBlockType(xx & 0xF, yy, zz & 0xF); + } catch (Exception x) { + return DynmapBlockState.AIR; + } + } + @Override + public Object getBlockTileEntityFieldAt(String fieldId, int xoff, + int yoff, int zoff) { + return null; + } + @Override + public long getInhabitedTicks() { + try { + return snap.getInhabitedTicks(); + } catch (Exception x) { + return 0; + } + } + @Override + public DynmapBlockState getBlockType() { + if (blk == null) { + blk = snap.getBlockType(bx, y, bz); + } + return blk; + } + } + + private class OurEndMapIterator extends OurMapIterator + { + OurEndMapIterator(int x0, int y0, int z0) + { + super(x0, y0, z0); + } + @Override + public final int getBlockSkyLight() + { + return 15; + } + } + /** + * Chunk cache for representing unloaded chunk (or air) + */ + private static class EmptyChunk extends ChunkSnapshot + { + public EmptyChunk() + { + super(256, 0, 0, 0, 0); + } + /* Need these for interface, but not used */ + @Override + public int getX() + { + return 0; + } + @Override + public int getZ() + { + return 0; + } + @Override + public final DynmapBlockState getBlockType(int x, int y, int z) + { + return DynmapBlockState.AIR; + } + @Override + public final int getBlockSkyLight(int x, int y, int z) + { + return 15; + } + @Override + public final int getBlockEmittedLight(int x, int y, int z) + { + return 0; + } + @Override + public final int getHighestBlockYAt(int x, int z) + { + return 0; + } + @Override + public int getBiome(int x, int z) + { + return -1; + } + @Override + public boolean isSectionEmpty(int sy) + { + return true; + } + } + + /** + * Chunk cache for representing generic stone chunk + */ + private static class PlainChunk extends ChunkSnapshot + { + private DynmapBlockState fill; + + PlainChunk(String fill) + { + super(256, 0, 0, 0, 0); + this.fill = DynmapBlockState.getBaseStateByName(fill); + } + /* Need these for interface, but not used */ + @Override + public int getX() + { + return 0; + } + @Override + public int getZ() + { + return 0; + } + @Override + public int getBiome(int x, int z) + { + return -1; + } + @Override + public final DynmapBlockState getBlockType(int x, int y, int z) + { + if (y < 64) + { + return fill; + } + + return DynmapBlockState.AIR; + } + @Override + public final int getBlockSkyLight(int x, int y, int z) + { + if (y < 64) + { + return 0; + } + + return 15; + } + @Override + public final int getBlockEmittedLight(int x, int y, int z) + { + return 0; + } + @Override + public final int getHighestBlockYAt(int x, int z) + { + return 64; + } + @Override + public boolean isSectionEmpty(int sy) + { + return (sy < 4); + } + } + + private static final EmptyChunk EMPTY = new EmptyChunk(); + private static final PlainChunk STONE = new PlainChunk(DynmapBlockState.STONE_BLOCK); + private static final PlainChunk OCEAN = new PlainChunk(DynmapBlockState.WATER_BLOCK); + + + public static void init() { + if (!init) + { + Field[] f = ServerChunkProvider.class.getDeclaredFields(); + + f = ServerWorld.class.getDeclaredFields(); + for(int i = 0; i < f.length; i++) { + if((updateEntityTick == null) && f[i].getType().isAssignableFrom(int.class)) { + updateEntityTick = f[i]; + //Log.info("Found updateEntityTick - " + f[i].getName()); + updateEntityTick.setAccessible(true); + } + } + + f = ChunkManager.class.getDeclaredFields(); + for(int i = 0; i < f.length; i++) { + if((chunksToRemove == null) && (f[i].getType().equals(Map.class))) { + chunksToRemove = f[i]; + //Log.info("Found chunksToRemove - " + f[i].getName()); + chunksToRemove.setAccessible(true); + } +// else if((pendingAnvilChunksCoordinates == null) && (f[i].getType().equals(it.unimi.dsi.fastutil.longs.LongSet.class))) { +// //Log.info("Found pendingAnvilChunksCoordinates - " + f[i].getName()); +// pendingAnvilChunksCoordinates = f[i]; +// pendingAnvilChunksCoordinates.setAccessible(true); +// } + } + if (updateEntityTick == null) { + Log.severe("ERROR: cannot find updateEntityTick - dynmap cannot drive entity cleanup when no players are active"); + } + + init = true; + } + } + + /** + * Construct empty cache + */ + public ForgeMapChunkCache() + { + init(); + } + + public void setChunks(ForgeWorld dw, List chunks) + { + this.dw = dw; + this.w = dw.getWorld(); + if(dw.isLoaded()) { + /* Check if world's provider is ServerChunkProvider */ + AbstractChunkProvider cp = this.w.getChunkProvider(); + + if (cp instanceof ServerChunkProvider) + { + cps = (ServerChunkProvider)cp; + } + else + { + Log.severe("Error: world " + dw.getName() + " has unsupported chunk provider"); + } + } + else { + chunks = new ArrayList(); + } + nsect = dw.worldheight >> 4; + this.chunks = chunks; + + /* Compute range */ + if (chunks.size() == 0) + { + this.x_min = 0; + this.x_max = 0; + this.z_min = 0; + this.z_max = 0; + x_dim = 1; + } + else + { + x_min = x_max = chunks.get(0).x; + z_min = z_max = chunks.get(0).z; + + for (DynmapChunk c : chunks) + { + if (c.x > x_max) + { + x_max = c.x; + } + + if (c.x < x_min) + { + x_min = c.x; + } + + if (c.z > z_max) + { + z_max = c.z; + } + + if (c.z < z_min) + { + z_min = c.z; + } + } + + x_dim = x_max - x_min + 1; + } + + snapcnt = x_dim * (z_max-z_min+1); + snaparray = new ChunkSnapshot[snapcnt]; + snaptile = new DynIntHashMap[snapcnt]; + isSectionNotEmpty = new boolean[snapcnt][]; + + } + + private static boolean didError = false; + + public CompoundNBT readChunk(int x, int z) { + try { + ChunkManager acl = cps.chunkManager; + + ChunkPos coord = new ChunkPos(x, z); + CompoundNBT rslt = acl.readChunk(coord); + if(rslt != null) { + rslt = rslt.getCompound("Level"); + // Don't load uncooked chunks + String stat = rslt.getString("Status"); + ChunkStatus cs = ChunkStatus.byName(stat); + if ((stat == null) || + // Needs to be at least lighted + (!cs.isAtLeast(ChunkStatus.LIGHT))) { + rslt = null; + } + } + //Log.info(String.format("loadChunk(%d,%d)=%s", x, z, (rslt != null) ? rslt.toString() : "null")); + return rslt; + } catch (Exception exc) { + Log.severe(String.format("Error reading chunk: %s,%d,%d", dw.getName(), x, z), exc); + return null; + } + } + + private Object getNBTValue(INBT v) { + Object val = null; + switch(v.getId()) { + case 1: // Byte + val = Byte.valueOf(((ByteNBT)v).getByte()); + break; + case 2: // Short + val = Short.valueOf(((ShortNBT)v).getShort()); + break; + case 3: // Int + val = Integer.valueOf(((IntNBT)v).getInt()); + break; + case 4: // Long + val = Long.valueOf(((LongNBT)v).getLong()); + break; + case 5: // Float + val = Float.valueOf(((FloatNBT)v).getFloat()); + break; + case 6: // Double + val = Double.valueOf(((DoubleNBT)v).getDouble()); + break; + case 7: // Byte[] + val = ((ByteArrayNBT)v).getByteArray(); + break; + case 8: // String + val = ((StringNBT)v).getString(); + break; + case 9: // List + ListNBT tl = (ListNBT) v; + ArrayList vlist = new ArrayList(); + int type = tl.func_230528_d__(); + for (int i = 0; i < tl.size(); i++) { + switch (type) { + case 5: + float fv = tl.getFloat(i); + vlist.add(fv); + break; + case 6: + double dv = tl.getDouble(i); + vlist.add(dv); + break; + case 8: + String sv = tl.getString(i); + vlist.add(sv); + break; + case 10: + CompoundNBT tc = tl.getCompound(i); + vlist.add(getNBTValue(tc)); + break; + case 11: + int[] ia = tl.getIntArray(i); + vlist.add(ia); + break; + } + } + val = vlist; + break; + case 10: // Map + CompoundNBT tc = (CompoundNBT) v; + HashMap vmap = new HashMap(); + for (Object t : tc.keySet()) { + String st = (String) t; + INBT tg = tc.get(st); + vmap.put(st, getNBTValue(tg)); + } + val = vmap; + break; + case 11: // Int[] + val = ((IntArrayNBT)v).getIntArray(); + break; + } + return val; + } + + private boolean isChunkVisible(DynmapChunk chunk) { + boolean vis = true; + if(visible_limits != null) { + vis = false; + for(VisibilityLimit limit : visible_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = true; + break; + } + } + } + if(vis && (hidden_limits != null)) { + for(VisibilityLimit limit : hidden_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = false; + break; + } + } + } + return vis; + } + + private boolean tryChunkCache(DynmapChunk chunk, boolean vis) { + /* Check if cached chunk snapshot found */ + ChunkSnapshot ss = null; + SnapshotRec ssr = DynmapPlugin.plugin.sscache.getSnapshot(dw.getName(), chunk.x, chunk.z, blockdata, biome, biomeraw, highesty); + if(ssr != null) { + ss = ssr.ss; + if (!vis) + { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) + { + ss = STONE; + } + else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) + { + ss = OCEAN; + } + else + { + ss = EMPTY; + } + } + int idx = (chunk.x-x_min) + (chunk.z - z_min)*x_dim; + snaparray[idx] = ss; + snaptile[idx] = ssr.tileData; + } + return (ssr != null); + } + + // Prep snapshot and add to cache + private SnapshotRec prepChunkSnapshot(DynmapChunk chunk, CompoundNBT nbt) { + ChunkSnapshot ss = new ChunkSnapshot(nbt, dw.worldheight); + DynIntHashMap tileData = new DynIntHashMap(); + + ListNBT tiles = nbt.getList("TileEntities", 10); + if(tiles == null) tiles = new ListNBT(); + /* Get tile entity data */ + List vals = new ArrayList(); + for(int tid = 0; tid < tiles.size(); tid++) { + CompoundNBT tc = tiles.getCompound(tid); + int tx = tc.getInt("x"); + int ty = tc.getInt("y"); + int tz = tc.getInt("z"); + int cx = tx & 0xF; + int cz = tz & 0xF; + DynmapBlockState blk = ss.getBlockType(cx, ty, cz); + String[] te_fields = HDBlockModels.getTileEntityFieldsNeeded(blk); + if(te_fields != null) { + vals.clear(); + for(String id: te_fields) { + INBT v = tc.get(id); /* Get field */ + if(v != null) { + Object val = getNBTValue(v); + if(val != null) { + vals.add(id); + vals.add(val); + } + } + } + if(vals.size() > 0) { + Object[] vlist = vals.toArray(new Object[vals.size()]); + tileData.put(getIndexInChunk(cx, ty, cz), vlist); + } + } + } + SnapshotRec ssr = new SnapshotRec(); + ssr.ss = ss; + ssr.tileData = tileData; + DynmapPlugin.plugin.sscache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr, blockdata, biome, biomeraw, highesty); + + return ssr; + } + + /** + * Read NBT data from loaded chunks - needs to be called from server/world thread to be safe + * @returns number loaded + */ + public int getLoadedChunks() { + int cnt = 0; + if(!dw.isLoaded()) { + isempty = true; + unloadChunks(); + return 0; + } + ListIterator iter = chunks.listIterator(); + while (iter.hasNext()) { + long startTime = System.nanoTime(); + DynmapChunk chunk = iter.next(); + int chunkindex = (chunk.x-x_min) + (chunk.z - z_min)*x_dim; + if (snaparray[chunkindex] != null) continue; // Skip if already processed + + boolean vis = isChunkVisible(chunk); + + /* Check if cached chunk snapshot found */ + if (tryChunkCache(chunk, vis)) { + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + cnt++; + } + // If chunk is loaded and not being unloaded, we're grabbing its NBT data + else if (cps.chunkExists(chunk.x, chunk.z)) { + ChunkSnapshot ss; + DynIntHashMap tileData; + if (vis) { // If visible + CompoundNBT nbt = ChunkSerializer.write((ServerWorld)w, cps.getChunk(chunk.x, chunk.z, false)); + if (nbt != null) nbt = nbt.getCompound("Level"); + SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); + ss = ssr.ss; + tileData = ssr.tileData; + } + else { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { + ss = STONE; + } + else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { + ss = OCEAN; + } + else { + ss = EMPTY; + } + tileData = new DynIntHashMap(); + } + snaparray[chunkindex] = ss; + snaptile[chunkindex] = tileData; + endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); + cnt++; + } + } + return cnt; + } + + @Override + public int loadChunks(int max_to_load) + { + return getLoadedChunks() + readChunks(max_to_load); + + } + + public int readChunks(int max_to_load) + { + if(!dw.isLoaded()) { + isempty = true; + unloadChunks(); + return 0; + } + + int cnt = 0; + + if (iterator == null) + { + iterator = chunks.listIterator(); + } + + DynmapCore.setIgnoreChunkLoads(true); + + // Load the required chunks. + while ((cnt < max_to_load) && iterator.hasNext()) + { + long startTime = System.nanoTime(); + + DynmapChunk chunk = iterator.next(); + + int chunkindex = (chunk.x-x_min) + (chunk.z - z_min)*x_dim; + + if (snaparray[chunkindex] != null) continue; // Skip if already processed + + boolean vis = isChunkVisible(chunk); + + /* Check if cached chunk snapshot found */ + if (tryChunkCache(chunk, vis)) { + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + } + else { + CompoundNBT nbt = readChunk(chunk.x, chunk.z); + // If read was good + if (nbt != null) { + ChunkSnapshot ss; + DynIntHashMap tileData; + // If hidden + if (!vis) { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { + ss = STONE; + } + else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { + ss = OCEAN; + } + else { + ss = EMPTY; + } + tileData = new DynIntHashMap(); + } + else { + // Prep snapshot + SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); + ss = ssr.ss; + tileData = ssr.tileData; + } + snaparray[chunkindex] = ss; + snaptile[chunkindex] = tileData; + endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); + } + else { + endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); + } + } + cnt++; + } + + DynmapCore.setIgnoreChunkLoads(false); + + if (iterator.hasNext() == false) /* If we're done */ + { + isempty = true; + + /* Fill missing chunks with empty dummy chunk */ + for (int i = 0; i < snaparray.length; i++) + { + if (snaparray[i] == null) + { + snaparray[i] = EMPTY; + } + else if (snaparray[i] != EMPTY) + { + isempty = false; + } + } + } + return cnt; + } + /** + * Test if done loading + */ + public boolean isDoneLoading() + { + if(!dw.isLoaded()) { + return true; + } + if (iterator != null) + { + return !iterator.hasNext(); + } + + return false; + } + /** + * Test if all empty blocks + */ + public boolean isEmpty() + { + return isempty; + } + /** + * Unload chunks + */ + public void unloadChunks() + { + if (snaparray != null) + { + for (int i = 0; i < snaparray.length; i++) + { + snaparray[i] = null; + } + + snaparray = null; + } + } + private void initSectionData(int idx) + { + isSectionNotEmpty[idx] = new boolean[nsect + 1]; + + if (snaparray[idx] != EMPTY) + { + for (int i = 0; i < nsect; i++) + { + if (snaparray[idx].isSectionEmpty(i) == false) + { + isSectionNotEmpty[idx][i] = true; + } + } + } + } + public boolean isEmptySection(int sx, int sy, int sz) + { + int idx = (sx - x_min) + (sz - z_min) * x_dim; + + if (isSectionNotEmpty[idx] == null) + { + initSectionData(idx); + } + + return !isSectionNotEmpty[idx][sy]; + } + + /** + * Get cache iterator + */ + public MapIterator getIterator(int x, int y, int z) + { + if (dw.getEnvironment().equals("the_end")) + { + return new OurEndMapIterator(x, y, z); + } + + return new OurMapIterator(x, y, z); + } + /** + * Set hidden chunk style (default is FILL_AIR) + */ + public void setHiddenFillStyle(HiddenChunkStyle style) + { + this.hidestyle = style; + } + /** + * Add visible area limit - can be called more than once + * Needs to be set before chunks are loaded + * Coordinates are block coordinates + */ + public void setVisibleRange(VisibilityLimit lim) { + if(visible_limits == null) + visible_limits = new ArrayList(); + visible_limits.add(lim); + } + /** + * Add hidden area limit - can be called more than once + * Needs to be set before chunks are loaded + * Coordinates are block coordinates + */ + public void setHiddenRange(VisibilityLimit lim) { + if(hidden_limits == null) + hidden_limits = new ArrayList(); + hidden_limits.add(lim); + } + @Override + public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) + { + this.biome = biome; + this.biomeraw = rawbiome; + this.highesty = highestblocky; + this.blockdata = blockdata; + return true; + } + @Override + public DynmapWorld getWorld() + { + return dw; + } + + static + { + Biome b[] = DynmapPlugin.getBiomeList(); + BiomeMap[] bm = BiomeMap.values(); + biome_to_bmap = new BiomeMap[256]; + + for (int i = 0; i < biome_to_bmap.length; i++) + { + biome_to_bmap[i] = BiomeMap.NULL; + } + + for (int i = 0; i < b.length; i++) + { + if(b[i] == null) continue; + + String bs = b[i].toString(); + + for (int j = 0; j < bm.length; j++) + { + if (bm[j].toString().equals(bs)) + { + biome_to_bmap[i] = bm[j]; + break; + } + } + } + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeWorld.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeWorld.java new file mode 100644 index 00000000..bc854060 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/ForgeWorld.java @@ -0,0 +1,234 @@ +package org.dynmap.forge_1_16_2; +/** + * Forge specific implementation of DynmapWorld + */ +import java.util.List; + +import net.minecraft.util.RegistryKey; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.gen.Heightmap.Type; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.IWorld; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import net.minecraft.world.border.WorldBorder; + +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapLocation; +import org.dynmap.DynmapWorld; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.Polygon; + +public class ForgeWorld extends DynmapWorld +{ + private IServerWorld world; + private final boolean skylight; + private final boolean isnether; + private final boolean istheend; + private final String env; + private DynmapLocation spawnloc = new DynmapLocation(); + private static int maxWorldHeight = 256; // Maximum allows world height + + public static int getMaxWorldHeight() { + return maxWorldHeight; + } + public static void setMaxWorldHeight(int h) { + maxWorldHeight = h; + } + + public static String getWorldName(IServerWorld w) { + RegistryKey rk = w.getWorld().func_234923_W_(); + if (rk == World.field_234918_g_) { // Overworld? + return w.getWorld().getServer().func_240793_aU_().getWorldName(); + } + else if (rk == World.field_234920_i_) { + return "DIM1"; + } + else if (rk == World.field_234919_h_) { + return "DIM-1"; + } + else { + return rk.func_240901_a_().getNamespace() + "_" + rk.func_240901_a_().getPath(); + } + } + + public ForgeWorld(IServerWorld w) + { + this(getWorldName(w), w.getWorld().getHeight(), + w.getWorld().getSeaLevel(), + w.getWorld().func_234923_W_() == World.field_234920_i_, + w.getWorld().func_234923_W_() == World.field_234919_h_, + w.getWorld().func_234923_W_().func_240901_a_().getPath()); + setWorldLoaded(w); + } + public ForgeWorld(String name, int height, int sealevel, boolean nether, boolean the_end, String deftitle) + { + super(name, (height > maxWorldHeight)?maxWorldHeight:height, sealevel); + world = null; + setTitle(deftitle); + isnether = nether; + istheend = the_end; + skylight = !(isnether || istheend); + + if (isnether) + { + env = "nether"; + } + else if (istheend) + { + env = "the_end"; + } + else + { + env = "normal"; + } + + } + /* Test if world is nether */ + @Override + public boolean isNether() + { + return isnether; + } + public boolean isTheEnd() + { + return istheend; + } + /* Get world spawn location */ + @Override + public DynmapLocation getSpawnLocation() + { + if(world != null) { + spawnloc.x = world.getWorldInfo().getSpawnX(); + spawnloc.y = world.getWorldInfo().getSpawnY(); + spawnloc.z = world.getWorldInfo().getSpawnZ(); + spawnloc.world = this.getName(); + } + return spawnloc; + } + /* Get world time */ + @Override + public long getTime() + { + if(world != null) + return world.getWorld().getGameTime(); + else + return -1; + } + /* World is storming */ + @Override + public boolean hasStorm() + { + if(world != null) + return world.getWorld().isRaining(); + else + return false; + } + /* World is thundering */ + @Override + public boolean isThundering() + { + if(world != null) + return world.getWorld().isThundering(); + else + return false; + } + /* World is loaded */ + @Override + public boolean isLoaded() + { + return (world != null); + } + /* Set world to unloaded */ + @Override + public void setWorldUnloaded() + { + getSpawnLocation(); + world = null; + } + /* Set world to loaded */ + public void setWorldLoaded(IServerWorld w) { + world = w; + this.sealevel = w.getSeaLevel(); // Read actual current sealevel from world + // Update lighting table + for (int i = 0; i < 16; i++) { + this.setBrightnessTableEntry(i, w.getWorld().func_230315_m_().func_236021_a_(i)); + } + } + /* Get light level of block */ + @Override + public int getLightLevel(int x, int y, int z) + { + if(world != null) + return world.getLight(new BlockPos(x, y, z)); + else + return -1; + } + /* Get highest Y coord of given location */ + @Override + public int getHighestBlockYAt(int x, int z) + { + if(world != null) { + return world.getWorld().getChunk(x >> 4, z >> 4).getHeightmap(Type.MOTION_BLOCKING).getHeight(x & 15, z & 15); + } + else + return -1; + } + /* Test if sky light level is requestable */ + @Override + public boolean canGetSkyLightLevel() + { + return skylight; + } + /* Return sky light level */ + @Override + public int getSkyLightLevel(int x, int y, int z) + { + if(world != null) { + return world.getLightFor(LightType.SKY, new BlockPos(x, y, z)); + } + else + return -1; + } + /** + * Get world environment ID (lower case - normal, the_end, nether) + */ + @Override + public String getEnvironment() + { + return env; + } + /** + * Get map chunk cache for world + */ + @Override + public MapChunkCache getChunkCache(List chunks) + { + if(world != null) { + ForgeMapChunkCache c = new ForgeMapChunkCache(); + c.setChunks(this, chunks); + return c; + } + return null; + } + + public World getWorld() + { + return world.getWorld(); + } + @Override + public Polygon getWorldBorder() { + if (world != null) { + WorldBorder wb = world.getWorldBorder(); + if ((wb != null) && (wb.getDiameter() < 5.9E7)) { + Polygon p = new Polygon(); + p.addVertex(wb.minX(), wb.minZ()); + p.addVertex(wb.minX(), wb.maxZ()); + p.addVertex(wb.maxX(), wb.maxZ()); + p.addVertex(wb.maxX(), wb.minZ()); + return p; + } + } + return null; + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/Proxy.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/Proxy.java new file mode 100644 index 00000000..52b8e4fe --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/Proxy.java @@ -0,0 +1,24 @@ +package org.dynmap.forge_1_16_2; + +import net.minecraft.server.MinecraftServer; + +/** + * Server side proxy - methods for creating and cleaning up plugin + */ +public class Proxy +{ + public Proxy() + { + } + public DynmapPlugin startServer(MinecraftServer srv) { + DynmapPlugin plugin = DynmapPlugin.plugin; + if (plugin == null) { + plugin = new DynmapPlugin(srv); + plugin.onEnable(); + } + return plugin; + } + public void stopServer(DynmapPlugin plugin) { + plugin.onDisable(); + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/SnapshotCache.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/SnapshotCache.java new file mode 100644 index 00000000..112995c4 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/SnapshotCache.java @@ -0,0 +1,191 @@ +package org.dynmap.forge_1_16_2; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.dynmap.utils.DynIntHashMap; + +public class SnapshotCache { + public static class SnapshotRec { + public ChunkSnapshot ss; + public DynIntHashMap tileData; + }; + + private CacheHashMap snapcache; + private ReferenceQueue refqueue; + private long cache_attempts; + private long cache_success; + private boolean softref; + + private static class CacheRec { + Reference ref; + boolean hasbiome; + boolean hasrawbiome; + boolean hasblockdata; + boolean hashighesty; + } + + @SuppressWarnings("serial") + public class CacheHashMap extends LinkedHashMap { + private int limit; + private IdentityHashMap, String> reverselookup; + + public CacheHashMap(int lim) { + super(16, (float)0.75, true); + limit = lim; + reverselookup = new IdentityHashMap, String>(); + } + protected boolean removeEldestEntry(Map.Entry last) { + boolean remove = (size() >= limit); + if(remove && (last != null) && (last.getValue() != null)) { + reverselookup.remove(last.getValue().ref); + } + return remove; + } + } + + /** + * Create snapshot cache + */ + public SnapshotCache(int max_size, boolean softref) { + snapcache = new CacheHashMap(max_size); + refqueue = new ReferenceQueue(); + this.softref = softref; + } + private String getKey(String w, int cx, int cz) { + return w + ":" + cx + ":" + cz; + } + /** + * Invalidate cached snapshot, if in cache + */ + public void invalidateSnapshot(String w, int x, int y, int z) { + String key = getKey(w, x>>4, z>>4); + synchronized(snapcache) { + CacheRec rec = snapcache.remove(key); + if(rec != null) { + snapcache.reverselookup.remove(rec.ref); + rec.ref.clear(); + } + } + //processRefQueue(); + } + /** + * Invalidate cached snapshot, if in cache + */ + public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1, int z1) { + for(int xx = (x0>>4); xx <= (x1>>4); xx++) { + for(int zz = (z0>>4); zz <= (z1>>4); zz++) { + String key = getKey(w, xx, zz); + synchronized(snapcache) { + CacheRec rec = snapcache.remove(key); + if(rec != null) { + snapcache.reverselookup.remove(rec.ref); + rec.ref.clear(); + } + } + } + } + //processRefQueue(); + } + /** + * Look for chunk snapshot in cache + */ + public SnapshotRec getSnapshot(String w, int chunkx, int chunkz, + boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { + String key = getKey(w, chunkx, chunkz); + processRefQueue(); + SnapshotRec ss = null; + CacheRec rec; + synchronized(snapcache) { + rec = snapcache.get(key); + if(rec != null) { + ss = rec.ref.get(); + if(ss == null) { + snapcache.reverselookup.remove(rec.ref); + snapcache.remove(key); + } + } + } + if(ss != null) { + if((blockdata && (!rec.hasblockdata)) || + (biome && (!rec.hasbiome)) || + (biomeraw && (!rec.hasrawbiome)) || + (highesty && (!rec.hashighesty))) { + ss = null; + } + } + cache_attempts++; + if(ss != null) cache_success++; + + return ss; + } + /** + * Add chunk snapshot to cache + */ + public void putSnapshot(String w, int chunkx, int chunkz, SnapshotRec ss, + boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { + String key = getKey(w, chunkx, chunkz); + processRefQueue(); + CacheRec rec = new CacheRec(); + rec.hasblockdata = blockdata; + rec.hasbiome = biome; + rec.hasrawbiome = biomeraw; + rec.hashighesty = highesty; + if (softref) + rec.ref = new SoftReference(ss, refqueue); + else + rec.ref = new WeakReference(ss, refqueue); + synchronized(snapcache) { + CacheRec prevrec = snapcache.put(key, rec); + if(prevrec != null) { + snapcache.reverselookup.remove(prevrec.ref); + } + snapcache.reverselookup.put(rec.ref, key); + } + } + /** + * Process reference queue + */ + private void processRefQueue() { + Reference ref; + while((ref = refqueue.poll()) != null) { + synchronized(snapcache) { + String k = snapcache.reverselookup.remove(ref); + if(k != null) { + snapcache.remove(k); + } + } + } + } + /** + * Get hit rate (percent) + */ + public double getHitRate() { + if(cache_attempts > 0) { + return (100.0*cache_success)/(double)cache_attempts; + } + return 0.0; + } + /** + * Reset cache stats + */ + public void resetStats() { + cache_attempts = cache_success = 0; + } + /** + * Cleanup + */ + public void cleanup() { + if(snapcache != null) { + snapcache.clear(); + snapcache.reverselookup.clear(); + snapcache.reverselookup = null; + snapcache = null; + } + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/VersionCheck.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/VersionCheck.java new file mode 100644 index 00000000..913b40e7 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/VersionCheck.java @@ -0,0 +1,97 @@ +package org.dynmap.forge_1_16_2; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.dynmap.DynmapCore; +import org.dynmap.Log; + +public class VersionCheck { + private static final String VERSION_URL = "http://mikeprimm.com/dynmap/releases.php"; + public static void runCheck(final DynmapCore core) { + new Thread(new Runnable() { + public void run() { + doCheck(core); + } + }).start(); + } + + private static int getReleaseVersion(String s) { + int index = s.lastIndexOf('-'); + if(index < 0) + index = s.lastIndexOf('.'); + if(index >= 0) + s = s.substring(0, index); + String[] split = s.split("\\."); + int v = 0; + try { + for(int i = 0; (i < split.length) && (i < 3); i++) { + v += Integer.parseInt(split[i]) << (8 * (2 - i)); + } + } catch (NumberFormatException nfx) {} + return v; + } + + private static int getBuildNumber(String s) { + int index = s.lastIndexOf('-'); + if(index < 0) + index = s.lastIndexOf('.'); + if(index >= 0) + s = s.substring(index+1); + try { + return Integer.parseInt(s); + } catch (NumberFormatException nfx) { + return 99999999; + } + } + + private static void doCheck(DynmapCore core) { + String pluginver = core.getDynmapPluginVersion(); + String platform = core.getDynmapPluginPlatform(); + String platver = core.getDynmapPluginPlatformVersion(); + if((pluginver == null) || (platform == null) || (platver == null)) + return; + HttpURLConnection conn = null; + String loc = VERSION_URL; + int cur_ver = getReleaseVersion(pluginver); + int cur_bn = getBuildNumber(pluginver); + try { + while((loc != null) && (!loc.isEmpty())) { + URL url = new URL(loc); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestProperty("User-Agent", "Dynmap (" + platform + "/" + platver + "/" + pluginver); + conn.connect(); + loc = conn.getHeaderField("Location"); + } + BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line = null; + while((line = rdr.readLine()) != null) { + String[] split = line.split(":"); + if(split.length < 4) continue; + /* If our platform and version, or wildcard platform version */ + if(split[0].equals(platform) && (split[1].equals("*") || split[1].equals(platver))) { + int recommended_ver = getReleaseVersion(split[2]); + int recommended_bn = getBuildNumber(split[2]); + if((recommended_ver > cur_ver) || ((recommended_ver == cur_ver) && (recommended_bn > cur_bn))) { /* Newer recommended build */ + Log.info("Version obsolete: new recommended version " + split[2] + " is available."); + } + else if(cur_ver > recommended_ver) { /* Running dev or prerelease? */ + int prerel_ver = getReleaseVersion(split[3]); + int prerel_bn = getBuildNumber(split[3]); + if((prerel_ver > cur_ver) || ((prerel_ver == cur_ver) && (prerel_bn > cur_bn))) { + Log.info("Version obsolete: new prerelease version " + split[3] + " is available."); + } + } + } + } + } catch (Exception x) { + Log.info("Error checking for latest version"); + } finally { + if(conn != null) { + conn.disconnect(); + } + } + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/FilePermissions.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/FilePermissions.java new file mode 100644 index 00000000..59c605df --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/FilePermissions.java @@ -0,0 +1,103 @@ +package org.dynmap.forge_1_16_2.permissions; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.minecraft.entity.player.PlayerEntity; + +import org.dynmap.ConfigurationNode; +import org.dynmap.Log; +import org.dynmap.forge_1_16_2.DynmapPlugin; + +public class FilePermissions implements PermissionProvider { + private HashMap> perms; + private Set defperms; + + public static FilePermissions create() { + File f = new File("dynmap/permissions.yml"); + if(!f.exists()) + return null; + ConfigurationNode cfg = new ConfigurationNode(f); + cfg.load(); + + Log.info("Using permissions.yml for access control"); + + return new FilePermissions(cfg); + } + + private FilePermissions(ConfigurationNode cfg) { + perms = new HashMap>(); + for(String k : cfg.keySet()) { + List p = cfg.getStrings(k, null); + if(p != null) { + k = k.toLowerCase(); + HashSet pset = new HashSet(); + for(String perm : p) { + pset.add(perm.toLowerCase()); + } + perms.put(k, pset); + if(k.equals("defaultuser")) { + defperms = pset; + } + } + } + } + + private boolean hasPerm(String player, String perm) { + Set ps = perms.get(player); + if((ps != null) && (ps.contains(perm))) { + return true; + } + if(defperms.contains(perm)) { + return true; + } + return false; + } + @Override + public Set hasOfflinePermissions(String player, Set perms) { + player = player.toLowerCase(); + HashSet rslt = new HashSet(); + if(DynmapPlugin.plugin.isOp(player)) { + rslt.addAll(perms); + } + else { + for(String p : perms) { + if(hasPerm(player, p)) { + rslt.add(p); + } + } + } + return rslt; + } + @Override + public boolean hasOfflinePermission(String player, String perm) { + player = player.toLowerCase(); + if(DynmapPlugin.plugin.isOp(player)) { + return true; + } + else { + return hasPerm(player, perm); + } + } + + @Override + public boolean has(PlayerEntity psender, String permission) { + if(psender != null) { + String n = psender.getName().getString().toLowerCase(); + return hasPerm(n, permission); + } + return true; + } + @Override + public boolean hasPermissionNode(PlayerEntity psender, String permission) { + if(psender != null) { + String player = psender.getName().getString().toLowerCase(); + return DynmapPlugin.plugin.isOp(player); + } + return false; + } + +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/OpPermissions.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/OpPermissions.java new file mode 100644 index 00000000..5b7a5ae6 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/OpPermissions.java @@ -0,0 +1,51 @@ +package org.dynmap.forge_1_16_2.permissions; + +import java.util.HashSet; +import java.util.Set; + +import net.minecraft.entity.player.PlayerEntity; + +import org.dynmap.Log; +import org.dynmap.forge_1_16_2.DynmapPlugin; + +public class OpPermissions implements PermissionProvider { + public HashSet usrCommands = new HashSet(); + + public OpPermissions(String[] usrCommands) { + for (String usrCommand : usrCommands) { + this.usrCommands.add(usrCommand); + } + Log.info("Using ops.txt for access control"); + } + + @Override + public Set hasOfflinePermissions(String player, Set perms) { + HashSet rslt = new HashSet(); + if(DynmapPlugin.plugin.isOp(player)) { + rslt.addAll(perms); + } + return rslt; + } + @Override + public boolean hasOfflinePermission(String player, String perm) { + return DynmapPlugin.plugin.isOp(player); + } + + @Override + public boolean has(PlayerEntity psender, String permission) { + if(psender != null) { + if(usrCommands.contains(permission)) { + return true; + } + return DynmapPlugin.plugin.isOp(psender.getEntity().getName().getString()); + } + return true; + } + @Override + public boolean hasPermissionNode(PlayerEntity psender, String permission) { + if(psender != null) { + return DynmapPlugin.plugin.isOp(psender.getEntity().getName().getString()); + } + return true; + } +} diff --git a/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/PermissionProvider.java b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/PermissionProvider.java new file mode 100644 index 00000000..6b251f58 --- /dev/null +++ b/forge-1.16.2/src/main/java/org/dynmap/forge_1_16_2/permissions/PermissionProvider.java @@ -0,0 +1,15 @@ +package org.dynmap.forge_1_16_2.permissions; + +import java.util.Set; + +import net.minecraft.entity.player.PlayerEntity; + +public interface PermissionProvider { + boolean has(PlayerEntity sender, String permission); + boolean hasPermissionNode(PlayerEntity sender, String permission); + + Set hasOfflinePermissions(String player, Set perms); + + boolean hasOfflinePermission(String player, String perm); + +} diff --git a/forge-1.16.2/src/main/resources/META-INF/accesstransformer.cfg b/forge-1.16.2/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 00000000..b9c90dd4 --- /dev/null +++ b/forge-1.16.2/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,3 @@ +public net.minecraft.world.server.ChunkManager field_219251_e # loaded chunk list +public net.minecraft.world.server.ChunkManager field_219252_f # loaded chunk list +public net.minecraft.world.biome.BiomeAmbience field_235206_c_ # waterColor \ No newline at end of file diff --git a/forge-1.16.2/src/main/resources/META-INF/mods.toml b/forge-1.16.2/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..007c788c --- /dev/null +++ b/forge-1.16.2/src/main/resources/META-INF/mods.toml @@ -0,0 +1,25 @@ +modLoader="javafml" +loaderVersion="[33,)" +issueTrackerURL="https://github.com/webbukkit/dynmap/issues" +[[mods]] +modId="dynmap" +version="${version}" +displayName="Dynmap" +authors="mikeprimm" +description=''' +Dynamic, Google-maps style rendered maps for your Minecraft server +''' + +[[dependencies.dynmap]] + modId="forge" + mandatory=true + versionRange="[33,)" + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT or SERVER + side="SERVER" +[[dependencies.dynmap]] + modId="minecraft" + mandatory=true + versionRange="[1.16.2]" + ordering="NONE" + side="SERVER" diff --git a/forge-1.16.2/src/main/resources/configuration.txt b/forge-1.16.2/src/main/resources/configuration.txt new file mode 100644 index 00000000..c66a91f3 --- /dev/null +++ b/forge-1.16.2/src/main/resources/configuration.txt @@ -0,0 +1,457 @@ +# All paths in this configuration file are relative to Dynmap's data-folder: minecraft_server/dynmap/ + +# All map templates are defined in the templates directory +# To use the HDMap very-low-res (2 ppb) map templates as world defaults, set value to vlowres +# The definitions of these templates are in normal-vlowres.txt, nether-vlowres.txt, and the_end-vlowres.txt +# To use the HDMap low-res (4 ppb) map templates as world defaults, set value to lowres +# The definitions of these templates are in normal-lowres.txt, nether-lowres.txt, and the_end-lowres.txt +# To use the HDMap hi-res (16 ppb) map templates (these can take a VERY long time for initial fullrender), set value to hires +# The definitions of these templates are in normal-hires.txt, nether-hires.txt, and the_end-hires.txt +# To use the HDMap low-res (4 ppb) map templates, with support for boosting resolution selectively to hi-res (16 ppb), set value to low_boost_hi +# The definitions of these templates are in normal-low_boost_hi.txt, nether-low_boost_hi.txt, and the_end-low_boost_hi.txt +# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to vhi-res (32 ppb), set value to hi_boost_vhi +# The definitions of these templates are in normal-hi_boost_vhi.txt, nether-hi_boost_vhi.txt, and the_end-hi_boost_vhi.txt +# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to xhi-res (64 ppb), set value to hi_boost_xhi +# The definitions of these templates are in normal-hi_boost_xhi.txt, nether-hi_boost_xhi.txt, and the_end-hi_boost_xhi.txt +deftemplatesuffix: hires + +# Map storage scheme: only uncommoent one 'type' value +# filetree: classic and default scheme: tree of files, with all map data under the directory indicated by 'tilespath' setting +# sqlite: single SQLite database file (this can get VERY BIG), located at 'dbfile' setting (default is file dynmap.db in data directory) +# mysql: MySQL database, at hostname:port in database, accessed via userid with password +storage: + # Filetree storage (standard tree of image files for maps) + type: filetree + # SQLite db for map storage (uses dbfile as storage location) + #type: sqlite + #dbfile: dynmap.db + # MySQL DB for map storage (at 'hostname':'port' in database 'database' using user 'userid' password 'password' and table prefix 'prefix' + #type: mysql + #hostname: localhost + #port: 3306 + #database: dynmap + #userid: dynmap + #password: dynmap + #prefix: "" + +components: + - class: org.dynmap.ClientConfigurationComponent + + - class: org.dynmap.InternalClientUpdateComponent + sendhealth: true + sendposition: true + allowwebchat: true + webchat-interval: 5 + hidewebchatip: false + trustclientname: false + includehiddenplayers: false + # (optional) if true, color codes in player display names are used + use-name-colors: false + # (optional) if true, player login IDs will be used for web chat when their IPs match + use-player-login-ip: true + # (optional) if use-player-login-ip is true, setting this to true will cause chat messages not matching a known player IP to be ignored + require-player-login-ip: false + # (optional) block player login IDs that are banned from chatting + block-banned-player-chat: true + # Require login for web-to-server chat (requires login-enabled: true) + webchat-requires-login: false + # If set to true, users must have dynmap.webchat permission in order to chat + webchat-permissions: false + # Limit length of single chat messages + chatlengthlimit: 256 + # # Optional - make players hidden when they are inside/underground/in shadows (#=light level: 0=full shadow,15=sky) + # hideifshadow: 4 + # # Optional - make player hidden when they are under cover (#=sky light level,0=underground,15=open to sky) + # hideifundercover: 14 + # # (Optional) if true, players that are crouching/sneaking will be hidden + hideifsneaking: false + # If true, player positions/status is protected (login with ID with dynmap.playermarkers.seeall permission required for info other than self) + protected-player-info: false + # If true, hide players with invisibility potion effects active + hide-if-invisiblity-potion: true + # If true, player names are not shown on map, chat, list + hidenames: false + #- class: org.dynmap.JsonFileClientUpdateComponent + # writeinterval: 1 + # sendhealth: true + # sendposition: true + # allowwebchat: true + # webchat-interval: 5 + # hidewebchatip: false + # includehiddenplayers: false + # use-name-colors: false + # use-player-login-ip: false + # require-player-login-ip: false + # block-banned-player-chat: true + # hideifshadow: 0 + # hideifundercover: 0 + # hideifsneaking: false + # # Require login for web-to-server chat (requires login-enabled: true) + # webchat-requires-login: false + # # If set to true, users must have dynmap.webchat permission in order to chat + # webchat-permissions: false + # # Limit length of single chat messages + # chatlengthlimit: 256 + # hide-if-invisiblity-potion: true + # hidenames: false + + - class: org.dynmap.SimpleWebChatComponent + allowchat: true + # If true, web UI users can supply name for chat using 'playername' URL parameter. 'trustclientname' must also be set true. + allowurlname: false + + # Note: this component is needed for the dmarker commands, and for the Marker API to be available to other plugins + - class: org.dynmap.MarkersComponent + type: markers + showlabel: false + enablesigns: false + # Default marker set for sign markers + default-sign-set: markers + # (optional) add spawn point markers to standard marker layer + showspawn: true + spawnicon: world + spawnlabel: "Spawn" + # (optional) layer for showing offline player's positions (for 'maxofflinetime' minutes after logoff) + showofflineplayers: false + offlinelabel: "Offline" + offlineicon: offlineuser + offlinehidebydefault: true + offlineminzoom: 0 + maxofflinetime: 30 + # (optional) layer for showing player's spawn beds + showspawnbeds: false + spawnbedlabel: "Spawn Beds" + spawnbedicon: bed + spawnbedhidebydefault: true + spawnbedminzoom: 0 + spawnbedformat: "%name%'s bed" + # (optional) Show world border (vanilla 1.8+) + showworldborder: true + worldborderlabel: "Border" + + - class: org.dynmap.ClientComponent + type: chat + allowurlname: false + - class: org.dynmap.ClientComponent + type: chatballoon + focuschatballoons: false + - class: org.dynmap.ClientComponent + type: chatbox + showplayerfaces: true + messagettl: 5 + # Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages + #scrollback: 100 + # Optional: set maximum number of lines visible for chatbox + #visiblelines: 10 + # Optional: send push button + sendbutton: false + - class: org.dynmap.ClientComponent + type: playermarkers + showplayerfaces: true + showplayerhealth: true + # If true, show player body too (only valid if showplayerfaces=true + showplayerbody: false + # Option to make player faces small - don't use with showplayerhealth + smallplayerfaces: false + # Optional - make player faces layer hidden by default + hidebydefault: false + # Optional - ordering priority in layer menu (low goes before high - default is 0) + layerprio: 0 + # Optional - label for player marker layer (default is 'Players') + label: "Players" + + #- class: org.dynmap.ClientComponent + # type: digitalclock + - class: org.dynmap.ClientComponent + type: link + + - class: org.dynmap.ClientComponent + type: timeofdayclock + showdigitalclock: true + #showweather: true + # Mouse pointer world coordinate display + - class: org.dynmap.ClientComponent + type: coord + label: "Location" + hidey: false + show-mcr: false + show-chunk: false + + # Note: more than one logo component can be defined + #- class: org.dynmap.ClientComponent + # type: logo + # text: "Dynmap" + # #logourl: "images/block_surface.png" + # linkurl: "http://forums.bukkit.org/threads/dynmap.489/" + # # Valid positions: top-left, top-right, bottom-left, bottom-right + # position: bottom-right + + #- class: org.dynmap.ClientComponent + # type: inactive + # timeout: 1800 # in seconds (1800 seconds = 30 minutes) + # redirecturl: inactive.html + # #showmessage: 'You were inactive for too long.' + + #- class: org.dynmap.TestComponent + # stuff: "This is some configuration-value" + +# Treat hiddenplayers.txt as a whitelist for players to be shown on the map? (Default false) +display-whitelist: false + +# How often a tile gets rendered (in seconds). +renderinterval: 1 + +# How many tiles on update queue before accelerate render interval +renderacceleratethreshold: 60 + +# How often to render tiles when backlog is above renderacceleratethreshold +renderaccelerateinterval: 0.2 + +# How many update tiles to work on at once (if not defined, default is 1/2 the number of cores) +tiles-rendered-at-once: 2 + +# If true, use normal priority threads for rendering (versus low priority) - this can keep rendering +# from starving on busy Windows boxes (Linux JVMs pretty much ignore thread priority), but may result +# in more competition for CPU resources with other processes +usenormalthreadpriority: true + +# Save and restore pending tile renders - prevents their loss on server shutdown or /reload +saverestorepending: true + +# Save period for pending jobs (in seconds): periodic saving for crash recovery of jobs +save-pending-period: 900 + +# Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds) +zoomoutperiod: 30 + +# Control whether zoom out tiles are validated on startup (can be needed if zoomout processing is interrupted, but can be expensive on large maps) +initial-zoomout-validate: true + +# Default delay on processing of updated tiles, in seconds. This can reduce potentially expensive re-rendering +# of frequently updated tiles (such as due to machines, pistons, quarries or other automation). Values can +# also be set on individual worlds and individual maps. +tileupdatedelay: 30 + +# Tile hashing is used to minimize tile file updates when no changes have occurred - set to false to disable +enabletilehash: true + +# Optional - hide ores: render as normal stone (so that they aren't revealed by maps) +#hideores: true + +# Optional - enabled BetterGrass style rendering of grass and snow block sides +#better-grass: true + +# Optional - enable smooth lighting by default on all maps supporting it (can be set per map as lighting option) +smooth-lighting: true + +# Optional - use world provider lighting table (good for custom worlds with custom lighting curves, like nether) +# false=classic Dynmap lighting curve +use-brightness-table: true + +# Optional - render specific block names using the textures and models of another block name: can be used to hide/disguise specific +# blocks (e.g. make ores look like stone, hide chests) or to provide simple support for rendering unsupported custom blocks +block-alias: +# "minecraft:quartz_ore": "stone" +# "diamond_ore": "coal_ore" + +# Default image format for HDMaps (png, jpg, jpg-q75, jpg-q80, jpg-q85, jpg-q90, jpg-q95, jpg-q100) +# Has no effect on maps with explicit format settings +image-format: jpg-q90 + +# use-generated-textures: if true, use generated textures (same as client); false is static water/lava textures +# correct-water-lighting: if true, use corrected water lighting (same as client); false is legacy water (darker) +# transparent-leaves: if true, leaves are transparent (lighting-wise): false is needed for some Spout versions that break lighting on leaf blocks +use-generated-textures: true +correct-water-lighting: true +transparent-leaves: true + +# ctm-support: if true, Connected Texture Mod (CTM) in texture packs is enabled (default) +ctm-support: true +# custom-colors-support: if true, Custom Colors in texture packs is enabled (default) +custom-colors-support: true + +# Control loading of player faces (if set to false, skins are never fetched) +#fetchskins: false + +# Control updating of player faces, once loaded (if faces are being managed by other apps or manually) +#refreshskins: false + +# Customize URL used for fetching player skins (%player% is macro for name) +skin-url: "http://skins.minecraft.net/MinecraftSkins/%player%.png" + +# Control behavior for new (1.0+) compass orientation (sunrise moved 90 degrees: east is now what used to be south) +# default is 'newrose' (preserve pre-1.0 maps, rotate rose) +# 'newnorth' is used to rotate maps and rose (requires fullrender of any HDMap map - same as 'newrose' for FlatMap or KzedMap) +compass-mode: newnorth + +# Triggers for automatic updates : blockupdate-with-id is debug for breaking down updates by ID:meta +# To disable, set just 'none' and comment/delete the rest +render-triggers: + - blockupdate + #- blockupdate-with-id + #- lightingupdate + - chunkpopulate + - chunkgenerate + #- none + +# Title for the web page - if not specified, defaults to the server's name (unless it is the default of 'Unknown Server') +#webpage-title: "My Awesome Server Map" + +# The path where the tile-files are placed. +tilespath: web/tiles + +# The path where the web-files are located. +webpath: web + +# The path were the /dynmapexp command exports OBJ ZIP files +exportpath: export + +# The network-interface the webserver will bind to (0.0.0.0 for all interfaces, 127.0.0.1 for only local access). +# If not set, uses same setting as server in server.properties (or 0.0.0.0 if not specified) +#webserver-bindaddress: 0.0.0.0 + +# The TCP-port the webserver will listen on. +webserver-port: 8123 + +# Maximum concurrent session on internal web server - limits resources used in Bukkit server +max-sessions: 30 + +# Disables Webserver portion of Dynmap (Advanced users only) +disable-webserver: false + +# Enable/disable having the web server allow symbolic links (true=compatible with existing code, false=more secure (default)) +allow-symlinks: true + +# Enable login support +login-enabled: false +# Require login to access website (requires login-enabled: true) +login-required: false + +# Period between tile renders for fullrender, in seconds (non-zero to pace fullrenders, lessen CPU load) +timesliceinterval: 0.0 + +# Maximum chunk loads per server tick (1/20th of a second) - reducing this below 90 will impact render performance, but also will reduce server thread load +maxchunkspertick: 200 + +# Progress report interval for fullrender/radiusrender, in tiles. Must be 100 or greater +progressloginterval: 100 + +# Parallel fullrender: if defined, number of concurrent threads used for fullrender or radiusrender +# Note: setting this will result in much more intensive CPU use, some additional memory use. Caution should be used when +# setting this to equal or exceed the number of physical cores on the system. +#parallelrendercnt: 4 + +# Interval the browser should poll for updates. +updaterate: 2000 + +# If nonzero, server will pause fullrender/radiusrender processing when 'fullrenderplayerlimit' or more users are logged in +fullrenderplayerlimit: 0 +# If nonzero, server will pause update render processing when 'updateplayerlimit' or more users are logged in +updateplayerlimit: 0 +# Target limit on server thread use - msec per tick +per-tick-time-limit: 50 +# If TPS of server is below this setting, update renders processing is paused +update-min-tps: 18.0 +# If TPS of server is below this setting, full/radius renders processing is paused +fullrender-min-tps: 18.0 +# If TPS of server is below this setting, zoom out processing is paused +zoomout-min-tps: 18.0 + +showplayerfacesinmenu: true + +# Control whether players that are hidden or not on current map are grayed out (true=yes) +grayplayerswhenhidden: true + +# Set sidebaropened: 'true' to pin menu sidebar opened permanently, 'pinned' to default the sidebar to pinned, but allow it to unpin +#sidebaropened: true + +# Customized HTTP response headers - add 'id: value' pairs to all HTTP response headers (internal web server only) +#http-response-headers: +# Access-Control-Allow-Origin: "my-domain.com" +# X-Custom-Header-Of-Mine: "MyHeaderValue" + +# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields +trusted-proxies: + - "127.0.0.1" + - "0:0:0:0:0:0:0:1" + +joinmessage: "%playername% joined" +quitmessage: "%playername% quit" +spammessage: "You may only chat once every %interval% seconds." +# format for messages from web: %playername% substitutes sender ID (typically IP), %message% includes text +webmsgformat: "&color;2[WEB] %playername%: &color;f%message%" + +# Control whether layer control is presented on the UI (default is true) +showlayercontrol: true + +# Enable checking for banned IPs via banned-ips.txt (internal web server only) +check-banned-ips: true + +# Default selection when map page is loaded +defaultzoom: 0 +defaultworld: world +defaultmap: flat +# (optional) Zoom level and map to switch to when following a player, if possible +#followzoom: 3 +#followmap: surface + +# If true, make persistent record of IP addresses used by player logins, to support web IP to player matching +persist-ids-by-ip: true + +# If true, map text to cyrillic +cyrillic-support: false + +# Messages to customize +msg: + maptypes: "Map Types" + players: "Players" + chatrequireslogin: "Chat Requires Login" + chatnotallowed: "You are not permitted to send chat messages" + hiddennamejoin: "Player joined" + hiddennamequit: "Player quit" + +# URL for client configuration (only need to be tailored for proxies or other non-standard configurations) +url: + # configuration URL + #configuration: "up/configuration" + # update URL + #update: "up/world/{world}/{timestamp}" + # sendmessage URL + #sendmessage: "up/sendmessage" + # login URL + #login: "up/login" + # register URL + #register: "up/register" + # tiles base URL + #tiles: "tiles/" + # markers base URL + #markers: "tiles/" + # Snapshot cache size, in chunks +snapshotcachesize: 500 +# Snapshot cache uses soft references (true), else weak references (false) +soft-ref-cache: true + +# Player enter/exit title messages for map markers +# +# Processing period - how often to check player positions vs markers - default is 1000ms (1 second) +#enterexitperiod: 1000 +# Title message fade in time, in ticks (0.05 second intervals) - default is 10 (1/2 second) +#titleFadeIn: 10 +# Title message stay time, in ticks (0.05 second intervals) - default is 70 (3.5 seconds) +#titleStay: 70 +# Title message fade out time, in ticks (0.05 seocnd intervals) - default is 20 (1 second) +#titleFadeOut: 20 +# Enter/exit messages use on screen titles (true - default), if false chat messages are sent instead +#enterexitUseTitle: true +# Set true if new enter messages should supercede pending exit messages (vs being queued in order), default false +#enterReplacesExits: true + +# Set to true to enable verbose startup messages - can help with debugging map configuration problems +# Set to false for a much quieter startup log +verbose: false + +# Enables debugging. +#debuggers: +# - class: org.dynmap.debug.LogDebugger +# Debug: dump blocks missing render data +dump-missing-blocks: false diff --git a/forge-1.16.2/src/main/resources/pack.mcmeta b/forge-1.16.2/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..ee818eac --- /dev/null +++ b/forge-1.16.2/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Dynmap resources", + "pack_format": 4 + } +} diff --git a/forge-1.16.2/src/main/resources/permissions.yml.example b/forge-1.16.2/src/main/resources/permissions.yml.example new file mode 100644 index 00000000..a25f9adc --- /dev/null +++ b/forge-1.16.2/src/main/resources/permissions.yml.example @@ -0,0 +1,27 @@ +# +# Sample permissions.yml for dynmap - trivial, flat-file based permissions for dynmap features +# To use, copy this file to dynmap/permissions.yml, and edit appropriate. File is YAML format. +# +# All operators have full permissions to all functions. +# All users receive the permissions under the 'defaultuser' section +# Specific users can be given more permissions by defining a section with their name containing their permisssions +# All permissions correspond to those documented here (https://github.com/webbukkit/dynmap/wiki/Permissions), but +# do NOT have the 'dynmap.' prefix when used here (e.g. 'dynmap.fullrender' permission is just 'fullrender' here). +# +defaultuser: + - render + - show.self + - hide.self + - sendtoweb + - stats + - marker.list + - marker.listsets + - marker.icons + - webregister + - webchat + #- marker.sign + +#playername1: +# - fullrender +# - cancelrender +# - radiusrender diff --git a/settings.gradle b/settings.gradle index 9cf9ada7..58e64321 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,6 +9,7 @@ include ':bukkit-helper' include ':dynmap-api' include ':DynmapCore' include ':DynmapCoreAPI' +include ':forge-1.16.2' include ':forge-1.16.1' include ':forge-1.15.2' include ':forge-1.14.4' @@ -26,6 +27,7 @@ project(':bukkit-helper').projectDir = "$rootDir/bukkit-helper" as File project(':dynmap-api').projectDir = "$rootDir/dynmap-api" as File project(':DynmapCore').projectDir = "$rootDir/DynmapCore" as File project(':DynmapCoreAPI').projectDir = "$rootDir/DynmapCoreAPI" as File +project(':forge-1.16.2').projectDir = "$rootDir/forge-1.16.2" as File project(':forge-1.16.1').projectDir = "$rootDir/forge-1.16.1" as File project(':forge-1.15.2').projectDir = "$rootDir/forge-1.15.2" as File project(':forge-1.14.4').projectDir = "$rootDir/forge-1.14.4" as File