diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 374458df..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: Bug Report -assignees: '' - ---- - -> template is **bold** -> sample data is *italicized* - -**Issue Description:** *Dynmap sample issue description. This description would include as much and as little detail necessary for us to understand the issue in its entirety.* - -* **Dynmap Version:** *dynmap-version* -* **Server Version:** *forge 1.16 build x, or paper 1.15.2 build x* -* **Pastebin of Configuration.txt:** *https://pastebin.com/5SETL3z2* -* **Server Host (if applicable):** *McProHosting, Shockbyte, Selfhosted, etc.* -* **Pastebin of crashlogs or other relevant logs:** *https://pastebin.com/crashcausedbydynmap* -* **Other Relevant Data/Screenshots:** *I'm using this texture pack and these plugins and my map is from alpha .8* -* **Steps to Replicate:** *Please be clear in this section so we can replicate your issue* - - [ ] *I have looked at all other issues and this is not a duplicate* - [ ] *I have been able to replicate this* diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index d64ceee9..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: Feature Request -assignees: '' - ---- - -> template is **bold** -> sample data is *italicized* - -**Feature Description:** *Dynmap sample feature request description. This description would include as much and as little detail necessary for us to understand the feature in its entirety.* - -* **Additional context:** *Please add any additional information you feel will help us understand the feature* \ No newline at end of file diff --git a/.github/workflows/gibberish.txt b/.github/workflows/gibberish.txt deleted file mode 100644 index 05b53072..00000000 --- a/.github/workflows/gibberish.txt +++ /dev/null @@ -1,62 +0,0 @@ -Subreddit -blockscanner -dynmap -Dynmap's -APIs -APL -Dynmap -Dynmap's -DynmapCore -DynmapCoreAPI -JRuby -macOS -Minecraft -PRs -PaperMC -WorldGuard -www -reflow -runtime -theose -DynampCore -DynmapCore -DynmapCoreAPI -Reddit -api -Bukkit -mikeprimm -michaelprimm -repo -https -Fi -Ki -Patreon -fi -ko -patreon -README -Gradle -gradlew -gradle -oldgradle -Curseforge -SpigotMC -PaperMC -backends -Minecraft -WorldGuard -reflow -PRs -JRuby -Primm -reddit -subreddit -gg -pqBpw -JDBC -JDK -ForgeGradle -Kosma -Kosma's -DEV -Modrinth diff --git a/.github/workflows/spellcheck.yaml b/.github/workflows/spellcheck.yaml deleted file mode 100644 index d4facf8a..00000000 --- a/.github/workflows/spellcheck.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: Checking for spelling errors - -on: - push: - -jobs: - spellcheck: - name: rojopolis/spellcheck - runs-on: ubuntu-20.04 - steps: - - name: actions/checkout - uses: actions/checkout@v2 - - name: rojopolis/spellcheck - actually doing something - uses: rojopolis/spellcheck-github-actions@0.29.0 diff --git a/.gitignore b/.gitignore index a3dfa441..4bb1cd75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,3 @@ -# Eclipse stuff -.classpath -.project -.settings - -# netbeans -/nbproject - -# idea -.idea/ - -*.launch - -# we use maven! -/build.xml - -# maven -/target - -# vim -.*.sw[a-p] - -# various other potential build files -/build -/bin -/dist -/manifest.mf -/run - -# Mac filesystem dust -/.DS_Store -/dependency-reduced-pom.xml - +build/ +.gradle/ *.log -/.gradle -/fabric-1.16.1_client.launch -/fabric-1.16.1_server.launch -/fabric-1.16.2_client.launch -/fabric-1.16.2_server.launch - diff --git a/.spellcheck.yaml b/.spellcheck.yaml deleted file mode 100644 index b75b3702..00000000 --- a/.spellcheck.yaml +++ /dev/null @@ -1,25 +0,0 @@ -spellchecker: aspell -matrix: -- name: Markdown - aspell: - lang: en - dictionary: - wordlists: - - .github/workflows/gibberish.txt - encoding: utf-8 - pipeline: - - pyspelling.filters.markdown: - markdown_extensions: - - pymdownx.extra: - - pyspelling.filters.html: - comments: true - attributes: - - title - - alt - ignores: - - ':matches(code, pre)' - - 'code' - - 'pre' - sources: - - '**/*.md' - default_encoding: utf-8 diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0be1c0c6..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "automatic", - "java.compile.nullAnalysis.mode": "automatic" -} \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 00000000..afda5a69 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,87 @@ +# Changelog — Dynmap NeoForge 1.21.1 Port + +All changes relative to the upstream `neoforge-1.20.6` module. + +--- + +## [2026-03-07] — Final clean state (pre-PR) + +### Reverted +- `touchChunk()` helper method extraction — inlined back to match 1.20.6 structure + - Was purely cosmetic; upstream CONTRIBUTING.md prohibits refactoring in PRs + - No behavior change, no runtime impact + +### Fixed — whitespace +- `ForgeMapChunkCache.java` — formatting-only diffs corrected to match 1.20.6 style + +--- + +## [2026-03-07] — canOcclude() deadlock fix + +### Changed +- `DynmapPlugin.initializeBlockStates()` (lines ~248, ~259): + - `isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` → `canOcclude()` + - `propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` → `isAir()` + - `isSolid()` → `canOcclude()` + +### Why +`isSolidRender()` / `propagatesSkylightDown()` / `isSolid()` all trigger a Guava +`LoadingCache` lookup in ModernFix's `reduce_blockstate_cache_rebuilds` mixin. During +world load with modernfix + ferritecore (present in ATM10), this causes a +`BlockState$Cache.` deadlock — lazy/deferred block state initialization enters +an infinite loop in VoxelShape calculation for certain complex mod block states. +The watchdog kills the server after ~60 seconds. + +`canOcclude()` and `isAir()` read precomputed booleans directly from the block state +with no cache involvement. Functionally equivalent for Dynmap's purposes, safe with +all mods including modernfix and ferritecore. + +### Test result +- ✅ ATM10 5.5, 445 mods, NeoForge 21.1.219 — 3/3 clean boots +- ✅ `[Dynmap] version 3.7-SNAPSHOT-Dev is enabled` +- ✅ 17 worlds loaded, web server started on 0.0.0.0:8123 + +--- + +## [2026-03-07] — Runtime fixes + build improvements + +### Fixed — API changes (1.21.1) +- `DynmapPlugin`: `ServerTickEvent` listener updated to `ServerTickEvent.Post` + - NeoForge 21.x made `ServerTickEvent` abstract; registering the base class no longer works +- `DynmapPlugin.initializeBlockStates()`: replaced `isSolidRender(null, ...)` with `isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)` + - 1.21.1 actually uses the BlockGetter parameter; passing null causes NPE + - (Superseded by canOcclude() fix above — kept here for history) +- `DynmapPlugin`: `getLastAvailable()` → `getLatestChunk()` — API renamed +- `DynmapPlugin`: `getStatus()` → `getPersistedStatus()` — API renamed +- `DynmapPlugin`: `ChatHandler` inner class → direct method with `addListener()` + - NeoForge 1.21.1 changed event registration; inner class pattern no longer supported +- `NBT.java`: `contains()` reimplemented with manual type checking (behavior changed in 1.21.1) +- `NBT.java`: `getAsString()` null safety added (behavior changed in 1.21.1) + +### Fixed — Build +- `build.gradle`: userdev plugin 7.0.133 → 7.1.20 (NeoForge 21.1.x requirement) +- `build.gradle`: NeoForge dep 20.6.62-beta → 21.1.219 +- `gradle-wrapper.properties`: 8.7 → 8.14 (required for userdev 7.1.20) +- All `fabric-*/build.gradle`: loom 1.6.11 → 1.8.13 (required for Gradle 8.14 compat) +- `neoforge.mods.toml`: loader/version ranges updated for 1.21.1 +- Access transformer: SRG names → mojmap names +- Heap caps: Gradle daemon `-Xmx2g`, forked javac `-Xmx3g` + +### ⚠️ Known discrepancy (from earlier commit message) +- An early commit message claimed `visibleChunkMap` was replaced with `getChunks()` +- **This was inaccurate.** `visibleChunkMap` is still in the code and works fine in NeoForge 1.21.1 + +--- + +## [2026-03-07] — Initial 1.21.1 compile fix pass + +### Fixed — Compile errors (43 total) +- All compile errors resolved from `neoforge-1.20.6` → `neoforge-1.21.1` baseline: + - `net.minecraftforge.*` → `net.neoforged.*` (full package migration) + - `MinecraftForge.EVENT_BUS` → `NeoForge.EVENT_BUS` + - Access transformer updated for 1.21.1 private field patterns + +--- + +## Format note +Entries are dated by session (not release), since this is a port-in-progress. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 60402cd8..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,107 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -Dynmap is a dynamic web mapping plugin/mod for Minecraft servers. It's a multi-platform project supporting Spigot/PaperMC, Forge, and Fabric across multiple Minecraft versions (1.12.2 - 1.21.x). - -## Build Commands - -```bash -# Build all platforms (requires JDK 21 as default) -./gradlew setup build - -# Build outputs go to /target directory - -# Build specific module (for faster iteration, but NOT for PR submissions) -./gradlew :DynmapCore:build - -# Run unit tests (DynmapCore only — JUnit 4) -./gradlew :DynmapCore:test - -# Forge 1.12.2 (requires JDK 8 - set JAVA_HOME accordingly) -cd oldgradle -./gradlew setup build -``` - -**JDK Requirements:** -- Default: JDK 21 -- Forge 1.12.2 (oldgradle): JDK 8 strictly required -- Runtime targets: JDK 8 (1.16-), JDK 16 (1.17.x), JDK 17 (1.18-1.20.4), JDK 21 (1.20.5+) - -**Build notes:** -- `gradle.properties` sets `org.gradle.parallel=false` and `org.gradle.daemon=false` — do not change these -- `snakeyaml` is pinned at 1.23 intentionally — newer versions break on Windows-encoded config files - -## Architecture - -### Module Structure - -**Core Shared Modules:** -- `DynmapCoreAPI/` - Stable public API for external plugins/mods (markers, mod support, rendering). Published to `repo.mikeprimm.com`. The `org.dynmap.renderer` package here defines `DynmapBlockState` — the central block state abstraction used everywhere. -- `DynmapCore/` - Internal shared implementation (NOT stable - subject to breaking changes) -- `dynmap-api/` - Bukkit-specific public API - -**Platform Implementations:** -- `spigot/` - Bukkit/PaperMC implementation (`DynmapPlugin.java`) -- `bukkit-helper-*` - Version-specific NMS code (one per MC version: 1.13-1.21) -- `fabric-*` - Fabric mod implementations (1.14.4-1.21.x) -- `forge-*` - Forge mod implementations (1.14.4-1.21.x); `forge-1.12.2` lives in `oldgradle/` - -### Dependency Flow -``` -External Plugins/Mods - ↓ -DynmapCoreAPI (stable, published to repo.mikeprimm.com) - ↓ -DynmapCore (internal, unstable) - ↓ -Platform-specific modules (Spigot, Fabric, Forge) -``` - -### Key Components in DynmapCore - -- `DynmapCore.java` — Main coordination hub (~3,100 lines); bootstrapped by each platform -- `MapManager.java` — Tile rendering orchestration; owns the render thread pool and `FullWorldRenderState` queue -- `hdmap/` — HD map rendering pipeline: - - `IsoHDPerspective` — Isometric raytrace engine (the hot rendering path) - - `HDBlockModels` / `HDScaledBlockModels` — Block geometry (patch/volumetric/scaled models) - - `TexturePack` / `TexturePackLoader` — Texture resolution from resource packs - - `hdmap/renderer/` — Custom block renderers (stairs, fences, doors, etc.) implementing `CustomRenderer` - - Shaders (`DefaultHDShader`, `CaveHDShader`, `TopoHDShader`, etc.) — post-process pixel color - - Lighting (`DefaultHDLighting`, `ShadowHDLighting`, etc.) — light level calculation -- `storage/` — Storage backends (FileTree, MySQL, MariaDB, PostgreSQL, SQLite, MSSQL, AWS S3) -- `web/` — Embedded Jetty 9 server with custom HTTP routing (no standard servlet container) -- `markers/impl/` — Full marker system implementation; public interface is in `DynmapCoreAPI` -- `utils/MapChunkCache` + `utils/MapIterator` — Abstract interfaces that each platform implements to feed world data into the renderer - -### Platform Integration Pattern - -Each platform module (Spigot `bukkit-helper-*`, Fabric, Forge) must implement: -- `MapChunkCache` — Loads and caches chunk data for a tile's required chunks -- `MapIterator` — Block-by-block iteration over the loaded chunk cache -- A platform entry point (e.g., `DynmapPlugin` for Spigot) that bootstraps `DynmapCore` - -The `bukkit-helper-*` modules contain version-specific NMS code; `spigot/` delegates to the appropriate helper at runtime via reflection. - -## Testing - -Unit tests exist in `DynmapCore/src/test/` (JUnit 4) covering `Matrix3D`, `Vector3D`, `IpAddressMatcher`, `DynIntHashMap`, and `BufferInputStream`. Run with `./gradlew :DynmapCore:test`. - -Full verification requires: -1. Building all platforms: `./gradlew setup build` AND `cd oldgradle && ./gradlew setup build` -2. Manual testing on target Minecraft server platforms - -## Critical Contribution Rules - -**PRs must build and test on ALL platforms including oldgradle. Changes to DynmapCore/DynmapCoreAPI require testing on all platforms.** - -- **Java 8 compatibility required** — Code must compile and run on Java 8 -- **Java only** — No Kotlin, Scala, or other JVM languages -- **No dependency updates** — Library versions are tied to platform compatibility -- **No platform-specific code** — Must work on Windows, Linux (x86/ARM), macOS, Docker -- **Small PRs only** — One feature per PR, no style/formatting changes -- **No mod-specific code** — Use Dynmap APIs instead; external mods should depend on DynmapCoreAPI -- **Apache License v2** — All code must be compatible -- **DynmapCoreAPI is the only stable API** — Do not add external dependencies on DynmapCore internals diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index f943524c..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,76 +0,0 @@ -## Do you need support? -Details about support for the project can be found here on the Wiki. You may also wish to use the [Subreddit](https://reddit.com/r/Dynmap/) or the [Discord](https://discord.gg/52pqBpw) to get support. Templates for asking support can be found in each location. - -## Have you found a bug? -Before reporting a bug or issue, please make sure you can replicate the issue and that the issue is directly related to the main dynmap branch (not one of the forks like dynmap-essentials, dynmap-blockscanner, etc) If so, please submit bug reports **ONLY TO THIS GITHUB** with the title `[BUG REPORT] Bug Report Title` - -## Contributing to Dynmap's Code? -The Dynmap team welcomes Pull Requests with fixes, new features, and new platform support. That said, the following rules apply: -- Ultimately, we reserve the right to accept or deny a PR for any reason: fact is, by accepting it, we're also accepting any of the problems with supporting it, -explaining it to users, and fixing current and future problems - if we don't think the PR is of value consistent with that cost, we'll probably not accept it. -- All PRs should be as small as they can be to accomplish the feature or fix being supplied. To that end: - - Do not lump multiple features into one PR - you'll be asked to split them up before they will be reviewed or accepted - - Do not make style changes, reflow code, pretty printing, or otherwise make formatting-only code changes. This makes the PR excessively large, - creating changes to be reviewed that don't actually do anything (but we have to review them to be sure they aren't being used to disguise security - compromises or other malicious code), and they create problems with the MANY people who fork Dynmap for the sake of doing PRs or their own private - custom builds - since all theose modified lines create merge conflicts - once again, with no actual function having been accomplished. If we decide - the code needs to be 'prettied up', it'll be done by the Dynmap team. -- Do not make changes to core code (anything in DynmapCore or DynmapCoreAPI) unless you're ready to build and test it on all supported platforms. Code that -breaks building of ANY supported platform will be rejected. -- Likewise, any Spigot related changes are expected to function correctly on all supported Spigot and PaperMC versions (currently 1.10.2 through 1.16.1). -- Do not include any code that involves platform specific native libraries or command line behaviors. Dynmap supports 32-bit and 64-bit, Windows, lots of -Linux versions (both x86 and ARM), MacOS, being used in Docker environments, and more - this is all about staying as 'pure Java' as the Minecraft server itself -is. If your PR includes platform specific dependencies that are not coded to handle working on all the above platforms properly, the PR will be rejected. -- Dynmap's code is Apache Public License v2 - do not include any code that is not compatible with this license. By contributing code, you are agreeing to -that code being subject to the APL v2. -- Do not include any code that unconditionally adds to Dynmap's hosting requirements - for example, support for a database can be added, but the use of the -database (which likely depends on a database server being deployed and configured by the user) cannot become an unconditional requirement in order to run -Dynmap. Features can add the option to exploit new or additional technologies, but cannot add unconditionally to the minimum requirements on the supported -platforms (which is what is needed to run the corresponding MC server, plus the Dynmap plugin or mod) -- Dynmap is built and supports running on Java 8 - it can run on newer versions, but any contributed code and dependencies MUST support being compiled and run -using just Java 8. -- Don't introduce other language dependencies - Java only: no Kotlin, Scala, JRuby, whatever. They just add runtime dependencies that most of the platforms lack, -and language skills above and beyond the Java language requirements the code base already mandates, which just creates obstacles to other people contributing. -- Similarly, do not update existing libraries and dependencies - these are often tied to the versions on various platforms, and updates will likely break runtime -- Do not include code specific to other plugins or mods. Dynmap has APIs for the purpose of avoiding the problem of working with other mods - there are many -'Dynmap-XXX' mods and plugins which use the APIs to provide support for other mods and plugins (WorldGuard, Nucleus, Citizens, dozens of others). Maintaining -interfaces in Dynmap particular to dozens of mods on multiple versions of multiple platforms is unmanageable, so we don't do it. The ONLY exception currently -are security mods - although, even for those, leverage of platform-standard security interfaces is always preferred (e.g. Sponge or Bukkit standard permissions) - -## Porting, Supporting Other Platforms, Customized Dynmap Builds -While Dynmap is open source, subject to the Apache Public License, v2, the Dynmap team does have specific policies and requirements for anyone that would -use the code here for anything except building contributions submitted back to this code base as Pull Requests (which is the only process by which code is accepted and can become part of a release supported by the Dynmap team). Other authorized uses include: - -- Building custom version of Dynmap for use on a personal or on a specific server, so long as this version is NOT distributed to other parties. -The modifying team agrees to not pursue support from the Dynmap team for this modified private version, but is otherwise not required to share the -modified source code - though doing so is encouraged. -- Building a modified version of Dynmap for otherwise unsupported platforms: in this event, the modified version MUST be for a platform or version -not yet (or no longer) supported by the Dynmap team. If the Dynmap team comes to support this platform or version, the modifying team must agree to -cease distribution of the unofficial version, unless otherwise authorized to continue doing so. Further: - - The team distributing the modified version must cite the origin of the Dynmap code, but must also clearly indicate that the version is NOT supported by - nor endorsed by the Dynmap team, and that ALL support should be directed through the team providing the modified version. - - Any modified version CANNOT be monetized or otherwise charged for, under any circumstances, nor can redistribution of it be limited or restricted. - - The modified code must continue to be Apache Public License v2, with no additional conditions or restrictions, including full public availability of the - modified source code. - - Any code from Dynmap used in such versions should be built from an appropriate fork, as DynampCore and other components (other than DynmapCoreAPI and - dynmap-api) are subject to breaking changes at any time, and the support messages in DynmapCore MUST be modified to refer to the supporting team (or, at - least, removed). The modified version should NOT refer to the Dynmap Discord nor to /r/Dynmap on Reddit for support. in any case. - - Any bugs or issues opened in conjunction with the use of the modified version on this repository will be closed without comment. - -Additions of new functions, including new platform support, in this official Dynmap code base MUST be fully contained within the PRs submitted to this -repository. Further, it is always expected than any updates will be built and tested across all relevant platforms - meaning any chances to shared code -components (DynmapCore, DynmapCoreAPI) MUST be successfully built and tested on ALL supported platforms (Forge, Spigot, etc). Changes which break -supported platforms will be rejected. - -The only interfaces published and maintained as 'stable' are the interfaces of the DynmapCoreAPI (cross platform) and dynmap-api (Bukkit/spigot specific) -libraries. All other components are NOT libraries - DynmapCore, in particular, is a shared code component across the various platforms, but is subject to -breaking changes without warning or consideration - any use of DynmapCore interfaces by code outside this repository is NOT supported, and will likely -result in breaking of such consuming code without warning and without apology. DynmapCore is an internal shared code component, not a library - please -treat it accordingly. - -Plugins or mods using the published APIs - DynmapCoreAPI (for all platforms) or dynmap-api (only for Spigot/Bukkit) - may access these components as -'compile' dependencies: DO NOT INTEGRATE THEM INTO YOUR PLUGIN - this will break Dynmap and/or other plugins when these interfaces are updated or -expanded. These libraries are published at https://repo.mikeprimm.com and will be updated each official release. - -## Want to support the dynmap team? -I've set up a coffee-fund jar (I believe in the theory that software developers are machines that turn caffeine into code), for anyone who wants to throw in some tips! I've got a Patreon here - https://www.patreon.com/dynmap, and for folks just looking to for a one-time coffee buy, hit my Ko-Fi at https://ko-fi.com/michaelprimm ! diff --git a/DynmapCore/.gitignore b/DynmapCore/.gitignore deleted file mode 100644 index 7657a96c..00000000 --- a/DynmapCore/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build/ -/bin/ \ No newline at end of file diff --git a/DynmapCore/build.gradle b/DynmapCore/build.gradle deleted file mode 100644 index d5955eeb..00000000 --- a/DynmapCore/build.gradle +++ /dev/null @@ -1,100 +0,0 @@ -description = "DynmapCore" - -apply plugin: 'eclipse' - -eclipse { - project { - name = "Dynmap(DynmapCore)" - } -} - -sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. - -dependencies { - implementation project(':DynmapCoreAPI') - implementation 'javax.servlet:javax.servlet-api:3.1' - implementation'org.eclipse.jetty:jetty-server:9.4.26.v20200117' - implementation 'org.eclipse.jetty:jetty-servlet:9.4.26.v20200117' - implementation 'com.googlecode.json-simple:json-simple:1.1.1' - implementation 'org.yaml:snakeyaml:1.23' // DON'T UPDATE - NEWER ONE TRIPS ON WINDOWS ENCODED FILES - implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20180219.1' - implementation 'org.postgresql:postgresql:42.2.18' - implementation 'io.github.linktosriram.s3lite:core:0.0.2-SNAPSHOT' - implementation 'io.github.linktosriram.s3lite:api:0.0.2-SNAPSHOT' - implementation 'io.github.linktosriram.s3lite:http-client-url-connection:0.0.2-SNAPSHOT' - implementation 'io.github.linktosriram.s3lite:http-client-spi:0.0.2-SNAPSHOT' - implementation 'io.github.linktosriram.s3lite:util:0.0.2-SNAPSHOT' - implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1' - implementation 'com.sun.xml.bind:jaxb-impl:3.0.0' - // Test dependencies (Java 8 compatible versions) - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.11.0' - testImplementation 'org.assertj:assertj-core:3.24.2' -} - -test { - useJUnit() - testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" - } -} - -processResources { - // replace stuff in mcmod.info, nothing else - filesMatching([ - 'core.yml', - 'lightings.txt', - 'perspectives.txt', - 'shaders.txt', - 'extracted/web/version.js', - 'extracted/web/index.html', - 'extracted/web/login.html', - ]) { - // replace version and mcversion - expand( - buildnumber: project.parent.ext.globals.buildNumber, - version: project.version - ) - } -} - -jar { - archiveClassifier = 'unshaded' -} - -shadowJar { - dependencies { - include(dependency('com.googlecode.json-simple:json-simple:')) - include(dependency('org.yaml:snakeyaml:')) - include(dependency('com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:')) - include(dependency('javax.servlet::')) - include(dependency('org.eclipse.jetty::')) - include(dependency('org.eclipse.jetty.orbit:javax.servlet:')) - include(dependency('org.postgresql:postgresql:')) - include(dependency('io.github.linktosriram.s3lite:core:')) - include(dependency('io.github.linktosriram.s3lite:api:')) - include(dependency('io.github.linktosriram.s3lite:http-client-url-connection:')) - include(dependency('io.github.linktosriram.s3lite:http-client-spi:')) - include(dependency('io.github.linktosriram.s3lite:util:')) - include(dependency('jakarta.xml.bind::')) - include(dependency('com.sun.xml.bind::')) - include(dependency(':DynmapCoreAPI')) - exclude("META-INF/maven/**") - exclude("META-INF/services/**") - } - relocate('org.json.simple', 'org.dynmap.json.simple') - relocate('org.yaml.snakeyaml', 'org.dynmap.snakeyaml') - relocate('org.eclipse.jetty', 'org.dynmap.jetty') - relocate('org.owasp.html', 'org.dynmap.org.owasp.html') - relocate('javax.servlet', 'org.dynmap.javax.servlet' ) - relocate('org.postgresql', 'org.dynmap.org.postgresql') - relocate('io.github.linktosriram.s3lite', 'org.dynmap.s3lite') - - destinationDirectory = file '../target' - archiveClassifier = '' -} - -artifacts { - archives shadowJar -} diff --git a/DynmapCore/src/.gitignore b/DynmapCore/src/.gitignore deleted file mode 100644 index e43b0f98..00000000 --- a/DynmapCore/src/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store diff --git a/DynmapCore/src/main/java/org/dynmap/AsynchronousQueue.java b/DynmapCore/src/main/java/org/dynmap/AsynchronousQueue.java deleted file mode 100644 index 42d4bed9..00000000 --- a/DynmapCore/src/main/java/org/dynmap/AsynchronousQueue.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.dynmap; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; - -public class AsynchronousQueue { - private Object lock = new Object(); - private Thread thread; - private LinkedBlockingQueue queue = new LinkedBlockingQueue(); - private Set set = new HashSet(); - private Handler handler; - private int dequeueTime; - private int accelDequeueTime; - public int accelDequeueThresh; - private int pendingcnt; - private int pendinglimit; - private boolean normalprio; - - public AsynchronousQueue(Handler handler, int dequeueTime, int accelDequeueThresh, int accelDequeueTime, int pendinglimit, boolean normalprio) { - this.handler = handler; - this.dequeueTime = dequeueTime; - this.accelDequeueTime = accelDequeueTime; - this.accelDequeueThresh = accelDequeueThresh; - if(pendinglimit < 1) pendinglimit = 1; - this.pendinglimit = pendinglimit; - this.normalprio = normalprio; - } - - public boolean push(T t) { - synchronized (lock) { - if (!set.add(t)) { - return false; - } - } - queue.offer(t); - return true; - } - - private T pop() { - try { - T t = queue.take(); - synchronized (lock) { - set.remove(t); - } - return t; - } catch (InterruptedException ix) { - return null; - } - } - - public boolean remove(T t) { - synchronized (lock) { - if (set.remove(t)) { - queue.remove(t); - return true; - } - } - return false; - } - - public int size() { - return set.size(); - } - - public List popAll() { - List s; - synchronized(lock) { - s = new ArrayList(queue); - queue.clear(); - set.clear(); - } - return s; - } - - public void start() { - synchronized (lock) { - thread = new Thread(new Runnable() { - @Override - public void run() { - running(); - } - }); - thread.setDaemon(true); - thread.start(); - try { - if(!normalprio) - thread.setPriority(Thread.MIN_PRIORITY); - } catch (SecurityException e) { - Log.info("Failed to set minimum priority for worker thread!"); - } - } - } - - public void stop() { - synchronized (lock) { - if (thread == null) - return; - Thread oldThread = thread; - thread = null; - - Log.info("Stopping map renderer..."); - - oldThread.interrupt(); - try { - oldThread.join(1000); - } catch (InterruptedException e) { - Log.info("Waiting for map renderer to stop is interrupted"); - } - } - } - - private void running() { - try { - while (Thread.currentThread() == thread) { - synchronized(lock) { - while(pendingcnt >= pendinglimit) { - try { - lock.wait(accelDequeueTime); - } catch (InterruptedException ix) { - if(Thread.currentThread() != thread) - return; - throw ix; - } - } - } - T t = pop(); - if (t != null) { - synchronized(lock) { - pendingcnt++; - } - handler.handle(t); - } - if(set.size() >= accelDequeueThresh) - sleep(accelDequeueTime); - else - sleep(dequeueTime); - } - - } catch (Exception ex) { - Log.severe("Exception on rendering-thread", ex); - } - } - - private boolean sleep(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - return false; - } - return true; - } - - public void done(T t) { - synchronized (lock) { - if(pendingcnt > 0) pendingcnt--; - lock.notifyAll(); - } - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/ChatEvent.java b/DynmapCore/src/main/java/org/dynmap/ChatEvent.java deleted file mode 100644 index 99f0a830..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ChatEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.dynmap; - -public class ChatEvent { - public String source; - public String name; - public String message; - public ChatEvent(String source, String name, String message) { - this.source = source; - this.name = name; - this.message = message; - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/Client.java b/DynmapCore/src/main/java/org/dynmap/Client.java deleted file mode 100644 index a8a956d8..00000000 --- a/DynmapCore/src/main/java/org/dynmap/Client.java +++ /dev/null @@ -1,339 +0,0 @@ -package org.dynmap; - -import java.io.IOException; -import java.io.Writer; -import java.util.Random; - -import org.json.simple.JSONAware; -import org.json.simple.JSONStreamAware; -import org.owasp.html.HtmlPolicyBuilder; -import org.owasp.html.PolicyFactory; -import org.owasp.html.Sanitizers; -import org.dynmap.common.DynmapChatColor; - -public class Client { - - public static class Update implements JSONAware, JSONStreamAware { - public long timestamp = System.currentTimeMillis(); - - @Override - public String toJSONString() { - return org.dynmap.web.Json.stringifyJson(this); - } - - @Override - public void writeJSONString(Writer w) throws IOException { - w.write(toJSONString()); - } - } - - public static class ChatMessage extends Update { - public String type = "chat"; - public String source; - public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones) - public String message; - public String account; - public String channel; - public ChatMessage(String source, String channel, String playerName, String message, String playeraccount) { - this.source = source; - if (ClientUpdateComponent.hideNames) - this.playerName = ""; - else if (ClientUpdateComponent.usePlayerColors) - this.playerName = Client.encodeColorInHTML(playerName); - else - this.playerName = Client.stripColor(playerName); - this.message = DynmapChatColor.stripColor(message); - this.account = playeraccount; - this.channel = channel; - } - @Override - public boolean equals(Object o) { - if(o instanceof ChatMessage) { - ChatMessage m = (ChatMessage)o; - return m.source.equals(source) && m.playerName.equals(playerName) && m.message.equals(message); - } - return false; - } - @Override - public int hashCode() { - return source.hashCode() ^ playerName.hashCode() ^ message.hashCode(); - } - } - - public static class PlayerJoinMessage extends Update { - public String type = "playerjoin"; - public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones) - public String account; - public PlayerJoinMessage(String playerName, String playeraccount) { - if (ClientUpdateComponent.hideNames) - this.playerName = ""; - else if (ClientUpdateComponent.usePlayerColors) - this.playerName = Client.encodeColorInHTML(playerName); - else - this.playerName = Client.stripColor(playerName); - this.account = playeraccount; - } - @Override - public boolean equals(Object o) { - if(o instanceof PlayerJoinMessage) { - PlayerJoinMessage m = (PlayerJoinMessage)o; - return m.playerName.equals(playerName); - } - return false; - } - @Override - public int hashCode() { - return account.hashCode(); - } - } - - public static class PlayerQuitMessage extends Update { - public String type = "playerquit"; - public String playerName; // Note: this needs to be client-safe HTML text (can include tags, but only sanitized ones) - public String account; - public PlayerQuitMessage(String playerName, String playeraccount) { - if (ClientUpdateComponent.hideNames) - this.playerName = ""; - else if (ClientUpdateComponent.usePlayerColors) - this.playerName = Client.encodeColorInHTML(playerName); - else - this.playerName = Client.stripColor(playerName); - this.account = playeraccount; - } - @Override - public boolean equals(Object o) { - if(o instanceof PlayerQuitMessage) { - PlayerQuitMessage m = (PlayerQuitMessage)o; - return m.playerName.equals(playerName); - } - return false; - } - @Override - public int hashCode() { - return account.hashCode(); - } - } - - public static class Tile extends Update { - public String type = "tile"; - public String name; - - public Tile(String name) { - this.name = name; - } - @Override - public boolean equals(Object o) { - if(o instanceof Tile) { - Tile m = (Tile)o; - return m.name.equals(name); - } - return false; - } - @Override - public int hashCode() { - return name.hashCode(); - } - } - - public static class DayNight extends Update { - public String type = "daynight"; - public boolean isday; - - public DayNight(boolean isday) { - this.isday = isday; - } - @Override - public boolean equals(Object o) { - if(o instanceof DayNight) { - return true; - } - return false; - } - @Override - public int hashCode() { - return 12345; - } - } - - public static class ComponentMessage extends Update { - public String type = "component"; - /* Each subclass must provide 'ctype' string for component 'type' */ - } - - // Strip color - assume we're returning safe html text - public static String stripColor(String s) { - s = DynmapChatColor.stripColor(s); /* Strip standard color encoding */ - /* Handle Essentials nickname encoding too */ - int idx = 0; - while((idx = s.indexOf('&', idx)) >= 0) { - char c = s.charAt(idx+1); /* Get next character */ - if(c == '&') { /* Another ampersand */ - s = s.substring(0, idx) + s.substring(idx+1); - } - else { - s = s.substring(0, idx) + s.substring(idx+2); - } - idx++; - } - // Apply sanitize policy before returning - return sanitizeHTML(s); - } - private static String[][] codes = { - { "0", "" }, - { "1", "" }, - { "2", "" }, - { "3", "" }, - { "4", "" }, - { "5", "" }, - { "6", "" }, - { "7", "" }, - { "8", "" }, - { "9", "" }, - { "a", "" }, - { "b", "" }, - { "c", "" }, - { "d", "" }, - { "e", "" }, - { "f", "" }, - { "l", "" }, - { "m", "" }, - { "n", "" }, - { "o", "" }, - { "r", "" } - }; - private static Random rnd = new Random(); - private static String rndchars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - // Replace color codes with corresponding "); // Substitute with hexcode - i = i + 12; //move past hex codes - } - } - break; - } - } - } - else if (c == '&') { // Essentials color code? - i++; // Move past it - c = s.charAt(i); - if (c == '&') { // Amp? - sb.append(c); - } - else { - if (c == 'k') { // Magic text? - magic = true; - } - else if (c == 'r') { // reset - magic = false; - } - for (int j = 0; j < codes.length; j++) { - if (codes[j][0].charAt(0) == c) { // Matching code? - sb.append(codes[j][1]); // Substitute - spancnt++; - break; - } - } - } - } - else if (magic) { - sb.append(rndchars.charAt(rnd.nextInt(rndchars.length()))); - } - else { - sb.append(c); - } - } - for (int i = 0; i < spancnt; i++) { - sb.append(""); - } - return sanitizeHTML(sb.toString()); - } - - private static PolicyFactory sanitizer = null; - private static PolicyFactory OLDTAGS = new HtmlPolicyBuilder().allowElements("center", "basefont", "hr").toFactory(); - public static String sanitizeHTML(String html) { - // Don't sanitize if null or no html markup - if ((html == null) || (html.indexOf('<') < 0)) return html; - PolicyFactory s = sanitizer; - if (s == null) { - // Generous but safe html formatting allowances - s = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(Sanitizers.IMAGES).and(Sanitizers.LINKS).and(Sanitizers.STYLES).and(Sanitizers.TABLES).and(OLDTAGS); - sanitizer = s; - } - return s.sanitize(html); - } - private static PolicyFactory stripper = null; - public static String stripHTML(String html) { - PolicyFactory s = stripper; - if (s == null) { - // Strip all taks - s = new HtmlPolicyBuilder().toFactory(); - stripper = s; - } - return s.sanitize(html); - } - // Encode plain text string for HTML presentation - public static String encodeForHTML(String text) { - String s = text != null ? text : ""; - StringBuilder str = new StringBuilder(); - - for (int j = 0; j < s.length(); j++) { - char c = s.charAt(j); - switch (c) { - case '"': - str.append("""); - break; - case '&': - str.append("&"); - break; - case '<': - str.append("<"); - break; - case '>': - str.append(">"); - break; - case '\'': - str.append("'"); - break; - default: - str.append(c); - break; - } - } - return str.toString(); - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/ClientComponent.java b/DynmapCore/src/main/java/org/dynmap/ClientComponent.java deleted file mode 100644 index f27cf284..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ClientComponent.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.dynmap; - -import static org.dynmap.JSONUtils.a; -import static org.dynmap.JSONUtils.s; - -import java.util.List; -import java.util.Map; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -public class ClientComponent extends Component { - private boolean disabled; - - public ClientComponent(final DynmapCore plugin, final ConfigurationNode configuration) { - super(plugin, configuration); - plugin.events.addListener("buildclientconfiguration", new Event.Listener() { - @Override - public void triggered(JSONObject root) { - if(!disabled) - buildClientConfiguration(root); - } - }); - } - - protected void disableComponent() { - disabled = true; - } - - protected void buildClientConfiguration(JSONObject root) { - JSONObject o = createClientConfiguration(); - a(root, "components", o); - } - - protected JSONObject createClientConfiguration() { - JSONObject o = convertMap(configuration); - o.remove("class"); - return o; - } - - protected static final JSONObject convertMap(Map m) { - JSONObject o = new JSONObject(); - for(Map.Entry entry : m.entrySet()) { - s(o, entry.getKey(), convert(entry.getValue())); - } - return o; - } - - @SuppressWarnings("unchecked") - protected static final JSONArray convertList(List l) { - JSONArray o = new JSONArray(); - for(Object entry : l) { - o.add(convert(entry)); - } - return o; - } - - @SuppressWarnings("unchecked") - protected static final Object convert(Object o) { - if (o instanceof Map) { - return convertMap((Map)o); - } else if (o instanceof List) { - return convertList((List)o); - } - return o; - } - -} diff --git a/DynmapCore/src/main/java/org/dynmap/ClientConfigurationComponent.java b/DynmapCore/src/main/java/org/dynmap/ClientConfigurationComponent.java deleted file mode 100644 index 26cb2f9f..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ClientConfigurationComponent.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.dynmap; - -import static org.dynmap.JSONUtils.a; -import static org.dynmap.JSONUtils.s; -import org.dynmap.Event.Listener; -import org.json.simple.JSONObject; - -public class ClientConfigurationComponent extends Component { - public ClientConfigurationComponent(final DynmapCore core, ConfigurationNode configuration) { - super(core, configuration); - core.events.addListener("buildclientconfiguration", new Listener() { - @Override - public void triggered(JSONObject t) { - ConfigurationNode c = core.configuration; - s(t, "confighash", core.getConfigHashcode()); - s(t, "updaterate", c.getFloat("updaterate", 1.0f)); - s(t, "showplayerfacesinmenu", c.getBoolean("showplayerfacesinmenu", true)); - s(t, "joinmessage", c.getString("joinmessage", "%playername% joined")); - s(t, "quitmessage", c.getString("quitmessage", "%playername% quit")); - s(t, "spammessage", c.getString("spammessage", "You may only chat once every %interval% seconds.")); - s(t, "webprefix", unescapeString(c.getString("webprefix", "[WEB] "))); - s(t, "defaultzoom", c.getInteger("defaultzoom", 0)); - s(t, "sidebaropened", c.getString("sidebaropened", "false")); - s(t, "dynmapversion", core.getDynmapPluginVersion()); - s(t, "coreversion", core.getDynmapCoreVersion()); - s(t, "cyrillic", c.getBoolean("cyrillic-support", false)); - s(t, "showlayercontrol", c.getString("showlayercontrol", "true")); - s(t, "grayplayerswhenhidden", c.getBoolean("grayplayerswhenhidden", true)); - s(t, "login-enabled", core.isLoginSupportEnabled()); - String sn = core.getServer().getServerName(); - if(sn.equals("Unknown Server")) - sn = "Minecraft Dynamic Map"; - s(t, "title", c.getString("webpage-title", sn)); - s(t, "msg-maptypes", c.getString("msg/maptypes", "Map Types")); - s(t, "msg-players", c.getString("msg/players", "Players")); - s(t, "msg-chatrequireslogin", c.getString("msg/chatrequireslogin", "Chat Requires Login")); - s(t, "msg-chatnotallowed", c.getString("msg/chatnotallowed", "You are not permitted to send chat messages")); - s(t, "msg-hiddennamejoin", c.getString("msg/hiddennamejoin", "Player joined")); - s(t, "msg-hiddennamequit", c.getString("msg/hiddennamequit", "Player quit")); - s(t, "maxcount", core.getMaxPlayers()); - - DynmapWorld defaultWorld = null; - String defmap = null; - a(t, "worlds", null); - for(DynmapWorld world : core.mapManager.getWorlds()) { - if (world.maps.size() == 0) continue; - if (defaultWorld == null) defaultWorld = world; - JSONObject wo = new JSONObject(); - s(wo, "name", world.getName()); - s(wo, "title", world.getTitle()); - s(wo, "protected", world.isProtected()); - DynmapLocation center = world.getCenterLocation(); - s(wo, "center/x", center.x); - s(wo, "center/y", center.y); - s(wo, "center/z", center.z); - s(wo, "extrazoomout", world.getExtraZoomOutLevels()); - s(wo, "sealevel", world.sealevel); - s(wo, "worldheight", world.worldheight); - a(t, "worlds", wo); - - for(MapType mt : world.maps) { - mt.buildClientConfiguration(wo, world); - if(defmap == null) defmap = mt.getName(); - } - } - s(t, "defaultworld", c.getString("defaultworld", defaultWorld == null ? "world" : defaultWorld.getName())); - s(t, "defaultmap", c.getString("defaultmap", defmap == null ? "surface" : defmap)); - if(c.getString("followmap", null) != null) - s(t, "followmap", c.getString("followmap")); - if(c.getInteger("followzoom",-1) >= 0) - s(t, "followzoom", c.getInteger("followzoom", 0)); - } - }); - } - -} diff --git a/DynmapCore/src/main/java/org/dynmap/ClientUpdateComponent.java b/DynmapCore/src/main/java/org/dynmap/ClientUpdateComponent.java deleted file mode 100644 index d45f0694..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ClientUpdateComponent.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.dynmap; - -import static org.dynmap.JSONUtils.a; -import static org.dynmap.JSONUtils.s; - -import java.util.List; -import org.dynmap.common.DynmapPlayer; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -public class ClientUpdateComponent extends Component { - private int hideifshadow; - private int hideifunder; - private boolean hideifsneaking; - private boolean hideifspectator; - private boolean hideifinvisiblepotion; - private boolean is_protected; - public static boolean usePlayerColors; - public static boolean hideNames; - - public ClientUpdateComponent(final DynmapCore core, ConfigurationNode configuration) { - super(core, configuration); - - hideNames = configuration.getBoolean("hidenames", false); - hideifshadow = configuration.getInteger("hideifshadow", 15); - hideifunder = configuration.getInteger("hideifundercover", 15); - hideifsneaking = configuration.getBoolean("hideifsneaking", false); - hideifspectator = configuration.getBoolean("hideifspectator", false); - hideifinvisiblepotion = configuration.getBoolean("hide-if-invisiblity-potion", true); - is_protected = configuration.getBoolean("protected-player-info", false); - usePlayerColors = configuration.getBoolean("use-name-colors", false); - if(is_protected) - core.player_info_protected = true; - - core.events.addListener("buildclientupdate", new Event.Listener() { - @Override - public void triggered(ClientUpdateEvent e) { - buildClientUpdate(e); - } - }); - } - - protected void buildClientUpdate(ClientUpdateEvent e) { - DynmapWorld world = e.world; - JSONObject u = e.update; - long since = e.timestamp; - String worldName = world.getName(); - boolean see_all = true; - - if(is_protected && (!e.include_all_users)) { - if(e.user != null) - see_all = core.getServer().checkPlayerPermission(e.user, "playermarkers.seeall"); - else - see_all = false; - } - if((e.include_all_users) && is_protected) { /* If JSON request AND protected, leave mark for script */ - s(u, "protected", true); - } - - s(u, "confighash", core.getConfigHashcode()); - - s(u, "servertime", world.getTime() % 24000); - s(u, "hasStorm", world.hasStorm()); - s(u, "isThundering", world.isThundering()); - - s(u, "players", new JSONArray()); - List players = core.playerList.getVisiblePlayers(); - for(DynmapPlayer p : players) { - boolean hide = false; - DynmapLocation pl = p.getLocation(); - DynmapWorld pw = core.getWorld(pl.world); - if(pw == null) { - hide = true; - } - JSONObject jp = new JSONObject(); - - s(jp, "type", "player"); - if (hideNames) - s(jp, "name", ""); - else if (usePlayerColors) - s(jp, "name", Client.encodeColorInHTML(p.getDisplayName())); - else - s(jp, "name", Client.stripColor(p.getDisplayName())); - s(jp, "account", p.getName()); - if((!hide) && (hideifshadow < 15)) { - if(pw.getLightLevel((int)pl.x, (int)pl.y, (int)pl.z) <= hideifshadow) { - hide = true; - } - } - if((!hide) && (hideifunder < 15)) { - if(pw.canGetSkyLightLevel()) { /* If we can get real sky level */ - if(pw.getSkyLightLevel((int)pl.x, (int)pl.y, (int)pl.z) <= hideifunder) { - hide = true; - } - } - else if(pw.isNether() == false) { /* Not nether */ - if(pw.getHighestBlockYAt((int)pl.x, (int)pl.z) > pl.y) { - hide = true; - } - } - } - if((!hide) && hideifsneaking && p.isSneaking()) { - hide = true; - } - if((!hide) && hideifspectator && p.isSpectator()) { - hide = true; - } - if((!hide) && is_protected && (!see_all)) { - if(e.user != null) { - hide = !core.testIfPlayerVisibleToPlayer(e.user, p.getName()); - } - else { - hide = true; - } - } - if((!hide) && hideifinvisiblepotion && p.isInvisible()) { - hide = true; - } - - /* Don't leak player location for world not visible on maps, or if sendposition disbaled */ - DynmapWorld pworld = MapManager.mapman.worldsLookup.get(pl.world); - /* Fix typo on 'sendpositon' to 'sendposition', keep bad one in case someone used it */ - if(configuration.getBoolean("sendposition", true) && configuration.getBoolean("sendpositon", true) && - (pworld != null) && pworld.sendposition && (!hide)) { - s(jp, "world", pl.world); - s(jp, "x", pl.x); - s(jp, "y", pl.y); - s(jp, "z", pl.z); - } - else { - s(jp, "world", "-some-other-bogus-world-"); - s(jp, "x", 0.0); - s(jp, "y", 64.0); - s(jp, "z", 0.0); - } - /* Only send health if enabled AND we're on visible world */ - if (configuration.getBoolean("sendhealth", false) && (pworld != null) && pworld.sendhealth && (!hide)) { - s(jp, "health", p.getHealth()); - s(jp, "armor", p.getArmorPoints()); - } - else { - s(jp, "health", 0); - s(jp, "armor", 0); - } - s(jp, "sort", p.getSortWeight()); - a(u, "players", jp); - } - List hidden = core.playerList.getHiddenPlayers(); - if(configuration.getBoolean("includehiddenplayers", false)) { - for(DynmapPlayer p : hidden) { - JSONObject jp = new JSONObject(); - s(jp, "type", "player"); - if (hideNames) - s(jp, "name", ""); - else if (usePlayerColors) - s(jp, "name", Client.encodeColorInHTML(p.getDisplayName())); - else - s(jp, "name", Client.stripColor(p.getDisplayName())); - s(jp, "account", p.getName()); - s(jp, "world", "-hidden-player-"); - s(jp, "x", 0.0); - s(jp, "y", 64.0); - s(jp, "z", 0.0); - s(jp, "health", 0); - s(jp, "armor", 0); - s(jp, "sort", p.getSortWeight()); - a(u, "players", jp); - } - s(u, "currentcount", core.getCurrentPlayers()); - } - else { - s(u, "currentcount", core.getCurrentPlayers() - hidden.size()); - } - - s(u, "updates", new JSONArray()); - for(Object update : core.mapManager.getWorldUpdates(worldName, since)) { - a(u, "updates", (Client.Update)update); - } - } - -} diff --git a/DynmapCore/src/main/java/org/dynmap/ClientUpdateEvent.java b/DynmapCore/src/main/java/org/dynmap/ClientUpdateEvent.java deleted file mode 100644 index 0fab4b0e..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ClientUpdateEvent.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.dynmap; - -import org.json.simple.JSONObject; - -public class ClientUpdateEvent { - public long timestamp; - public DynmapWorld world; - public JSONObject update; - public String user; - public boolean include_all_users; - - public ClientUpdateEvent(long timestamp, DynmapWorld world, JSONObject update) { - this.timestamp = timestamp; - this.world = world; - this.update = update; - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/Color.java b/DynmapCore/src/main/java/org/dynmap/Color.java deleted file mode 100644 index 2339c82d..00000000 --- a/DynmapCore/src/main/java/org/dynmap/Color.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.dynmap; - -/** - * Simple replacement for java.awt.Color for dynmap - it's not an invariant, so we don't make millions - * of them during rendering - */ -public class Color { - /* ARGB value */ - private int val; - - public static final int TRANSPARENT = 0; - - public Color(int red, int green, int blue, int alpha) { - setRGBA(red, green, blue, alpha); - } - public Color(int red, int green, int blue) { - setRGBA(red, green, blue, 0xFF); - } - public Color() { - setTransparent(); - } - public final int getRed() { - return (val >> 16) & 0xFF; - } - public final int getGreen() { - return (val >> 8) & 0xFF; - } - public final int getBlue() { - return val & 0xFF; - } - public final int getAlpha() { - return ((val >> 24) & 0xFF); - } - public final boolean isTransparent() { - return ((val & 0xFF000000) == TRANSPARENT); - } - public final void setTransparent() { - val = TRANSPARENT; - } - public final void setGrayscale() { - int alpha = val & 0xFF000000; - int num = (((val >> 16) & 0xFF) * 76) - + (((val >> 8) & 0xFF) * 151) - + (( val & 0xFF) * 28); - // weights sum to 255, so num ∈ [0, 65025]; fast /255 via shift - int gray = (num + (num >> 8) + 1) >> 8; - val = alpha | (gray << 16) | (gray << 8) | gray; - } - public final void setColor(Color c) { - val = c.val; - } - public final void setRGBA(int red, int green, int blue, int alpha) { - val = ((alpha & 0xFF) << 24) | ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF); - } - public final int getARGB() { - return val; - } - public final void setARGB(int c) { - val = c; - } - public final int getComponent(int idx) { - return 0xFF & (val >> ((3-idx)*8)); - } - public final void setAlpha(int v) { - val = (val & 0x00FFFFFF) | (v << 24); - } - public final void scaleColor(Color minimum, Color maximum) { - int alpha = (val >> 24) & 0xFF; - int red = (val >> 16) & 0xFF; - int green = (val >> 8) & 0xFF; - int blue = val & 0xFF; - red = minimum.getRed() + ((maximum.getRed() - minimum.getRed()) * red) / 256; - green = minimum.getGreen() + ((maximum.getGreen() - minimum.getGreen()) * green) / 256; - blue = minimum.getBlue() + ((maximum.getBlue() - minimum.getBlue()) * blue) / 256; - setRGBA(red, green, blue, alpha); - } - /** - * Scale each color component, based on the corresponding component - * @param c - color to blend - */ - public final void blendColor(Color c) { - blendColor(c.val); - } - /** - * Scale each color component, based on the corresponding component - * @param argb - ARGB to blend - */ - public final void blendColor(int argb) { - val = (mulDiv255(val >>> 24, argb >>> 24 ) << 24) - | (mulDiv255((val >> 16) & 0xFF, (argb >> 16) & 0xFF) << 16) - | (mulDiv255((val >> 8) & 0xFF, (argb >> 8) & 0xFF) << 8) - | mulDiv255( val & 0xFF, argb & 0xFF); - } - /** - * Scale each color component, based on the corresponding component - * @param argb0 - first color - * @param argb1 second color - * @return blended color - */ - public static final int blendColor(int argb0, int argb1) { - return (mulDiv255(argb0 >>> 24, argb1 >>> 24 ) << 24) - | (mulDiv255((argb0 >> 16) & 0xFF, (argb1 >> 16) & 0xFF) << 16) - | (mulDiv255((argb0 >> 8) & 0xFF, (argb1 >> 8) & 0xFF) << 8) - | mulDiv255( argb0 & 0xFF, argb1 & 0xFF); - } - /** - * Scale the RGB channels by scale/256, leaving alpha unchanged. - * Equivalent to setRGBA(getRed()*scale>>8, getGreen()*scale>>8, getBlue()*scale>>8, getAlpha()) - * but avoids redundant unpack/repack of the alpha channel. - * @param scale - scale factor 0..256 (256 = full brightness) - */ - public final void scaleRGB(int scale) { - val = (val & 0xFF000000) - | ((((val >> 16) & 0xFF) * scale >> 8) << 16) - | ((((val >> 8) & 0xFF) * scale >> 8) << 8) - | ((val & 0xFF) * scale >> 8); - } - /** - * Fast multiply-then-divide-by-255 for two values a, b each in [0, 255]. - * Returns floor(a*b/255), equivalent to the standard integer division but - * computed with shifts only: (x + (x >> 8) + 1) >> 8 where x = a * b. - */ - private static int mulDiv255(int a, int b) { - int x = a * b; - return (x + (x >> 8) + 1) >> 8; - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/ColorScheme.java b/DynmapCore/src/main/java/org/dynmap/ColorScheme.java deleted file mode 100644 index cfbdc778..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ColorScheme.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.dynmap; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Scanner; - -import org.dynmap.common.BiomeMap; -import org.dynmap.debug.Debug; -import org.dynmap.renderer.DynmapBlockState; - -public class ColorScheme { - private static final HashMap cache = new HashMap(); - - public String name; - /* Switch to arrays - faster than map */ - public final Color[][] colors; /* [global-state-idx][step] */ - public final Color[][] biomecolors; /* [Biome.ordinal][step] */ - public final Color[][] raincolors; /* [rain * 63][step] */ - public final Color[][] tempcolors; /* [temp * 63][step] */ - - public ColorScheme(String name, Color[][] colors, Color[][] biomecolors, Color[][] raincolors, - Color[][] tempcolors) { - this.name = name; - this.colors = colors; - this.biomecolors = biomecolors; - this.raincolors = raincolors; - this.tempcolors = tempcolors; - } - - private static File getColorSchemeDirectory(DynmapCore core) { - return new File(core.getDataFolder(), "colorschemes"); - } - - public static ColorScheme getScheme(DynmapCore core, String name) { - if (name == null) - name = "default"; - ColorScheme scheme = cache.get(name); - if (scheme == null) { - scheme = loadScheme(core, name); - cache.put(name, scheme); - } - return scheme; - } - - public static ColorScheme loadScheme(DynmapCore core, String name) { - File colorSchemeFile = new File(getColorSchemeDirectory(core), name + ".txt"); - Color[][] colors = new Color[DynmapBlockState.getGlobalIndexMax()][]; - Color[][] biomecolors = new Color[BiomeMap.values().length][]; - Color[][] raincolors = new Color[64][]; - Color[][] tempcolors = new Color[64][]; - - /* Default the biome color */ - for (int i = 0; i < biomecolors.length; i++) { - Color[] c = new Color[5]; - int red = 0x80 | (0x40 * ((i >> 0) & 1)) | (0x20 * ((i >> 3) & 1)) | (0x10 * ((i >> 6) & 1)); - int green = 0x80 | (0x40 * ((i >> 1) & 1)) | (0x20 * ((i >> 4) & 1)) | (0x10 * ((i >> 7) & 1)); - int blue = 0x80 | (0x40 * ((i >> 2) & 1)) | (0x20 * ((i >> 5) & 1)); - c[0] = new Color(red, green, blue); - c[3] = new Color(red * 4 / 5, green * 4 / 5, blue * 4 / 5); - c[1] = new Color(red / 2, green / 2, blue / 2); - c[2] = new Color(red * 2 / 5, green * 2 / 5, blue * 2 / 5); - c[4] = new Color((c[1].getRed() + c[3].getRed()) / 2, (c[1].getGreen() + c[3].getGreen()) / 2, - (c[1].getBlue() + c[3].getBlue()) / 2, (c[1].getAlpha() + c[3].getAlpha()) / 2); - - biomecolors[i] = c; - } - - InputStream stream; - // Get defaults from biome_rainfall_temp.txt - let custom file override after - File files[] = { - new File(getColorSchemeDirectory(core), "biome_rainfall_temp.txt"), - new File(getColorSchemeDirectory(core), "default.txt"), - colorSchemeFile }; - try { - for (int fidx = 0; fidx < files.length; fidx++) { - Debug.debug("Loading colors from '" + files[fidx] + "' for " + name + "..."); - stream = new FileInputStream(files[fidx]); - - Scanner scanner = new Scanner(stream); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith("#") || line.equals("")) { - continue; - } - /* Make parser less pedantic - tabs or spaces should be fine */ - String[] split = line.split("[\t ]"); - int cnt = 0; - for (String s : split) { - if (s.length() > 0) - cnt++; - } - String[] nsplit = new String[cnt]; - cnt = 0; - for (String s : split) { - if (s.length() > 0) { - nsplit[cnt] = s; - cnt++; - } - } - split = nsplit; - if (split.length < 17) { - continue; - } - Integer id = null; - boolean isbiome = false; - boolean istemp = false; - boolean israin = false; - DynmapBlockState state = null; - int idx = split[0].indexOf(':'); - if (idx > 0) { /* ID:data - data color OR blockstate - data color */ - String[] vsplit = split[0].split("[\\[\\]]"); - // Log.info(String.format("split[0]=%s,vsplit[0]=%s,vsplit[1]=%s", split[0], - // vsplit[0], vsplit.length > 1 ? vsplit[1] : "")); - if (vsplit.length > 1) { - state = DynmapBlockState.getStateByNameAndState(vsplit[0], vsplit[1]); - } else { - state = DynmapBlockState.getBaseStateByName(vsplit[0]); - } - } else if (split[0].charAt(0) == '[') { /* Biome color data */ - String bio = split[0].substring(1); - idx = bio.indexOf(']'); - if (idx >= 0) - bio = bio.substring(0, idx); - isbiome = true; - id = -1; - BiomeMap[] bm = BiomeMap.values(); - for (int i = 0; i < bm.length; i++) { - if (bm[i].getId().equalsIgnoreCase(bio)) { - id = i; - break; - } else if (bio.equalsIgnoreCase("BIOME_" + i)) { - id = i; - break; - } - } - if (id < 0) { /* Not biome - check for rain or temp */ - if (bio.startsWith("RAINFALL-")) { - try { - double v = Double.parseDouble(bio.substring(9)); - if ((v >= 0) && (v <= 1.00)) { - id = (int) (v * 63.0); - israin = true; - } - } catch (NumberFormatException nfx) { - } - } else if (bio.startsWith("TEMPERATURE-")) { - try { - double v = Double.parseDouble(bio.substring(12)); - if ((v >= 0) && (v <= 1.00)) { - id = (int) (v * 63.0); - istemp = true; - } - } catch (NumberFormatException nfx) { - } - } - } - } else { - id = Integer.parseInt(split[0]); - state = DynmapBlockState.getStateByLegacyBlockID(id); - } - - Color[] c = new Color[5]; - - /* store colors by raycast sequence number */ - c[0] = new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), - Integer.parseInt(split[4])); - c[3] = new Color(Integer.parseInt(split[5]), Integer.parseInt(split[6]), Integer.parseInt(split[7]), - Integer.parseInt(split[8])); - c[1] = new Color(Integer.parseInt(split[9]), Integer.parseInt(split[10]), - Integer.parseInt(split[11]), Integer.parseInt(split[12])); - c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), - Integer.parseInt(split[15]), Integer.parseInt(split[16])); - /* Blended color - for 'smooth' option on flat map */ - c[4] = new Color((c[1].getRed() + c[3].getRed()) / 2, (c[1].getGreen() + c[3].getGreen()) / 2, - (c[1].getBlue() + c[3].getBlue()) / 2, (c[1].getAlpha() + c[3].getAlpha()) / 2); - - if (isbiome) { - if (istemp) { - tempcolors[id] = c; - } else if (israin) { - raincolors[id] = c; - } else if ((id >= 0) && (id < biomecolors.length)) - biomecolors[id] = c; - } else if (state != null) { - int stateid = state.globalStateIndex; - colors[stateid] = c; - } - } - scanner.close(); - } - /* Last, push base color into any open slots in data colors list */ - for (int i = 0; i < colors.length; i++) { - if (colors[i] == null) { - DynmapBlockState bs = DynmapBlockState.getStateByGlobalIndex(i); // Get state - DynmapBlockState bsbase = bs.baseState; - if ((bsbase != null) && (colors[bsbase.globalStateIndex] != null)) { - colors[i] = colors[bsbase.globalStateIndex]; - } - } - } - /* And interpolate any missing rain and temperature colors */ - interpolateColorTable(tempcolors); - interpolateColorTable(raincolors); - } catch (RuntimeException e) { - Log.severe("Could not load colors '" + name + "' ('" + colorSchemeFile + "').", e); - return null; - } catch (FileNotFoundException e) { - Log.severe("Could not load colors '" + name + "' ('" + colorSchemeFile + "'): File not found.", e); - } - return new ColorScheme(name, colors, biomecolors, raincolors, tempcolors); - } - - public static void interpolateColorTable(Color[][] c) { - int idx = -1; - for (int k = 0; k < c.length; k++) { - if (c[k] == null) { /* Missing? */ - if ((idx >= 0) && (k == (c.length - 1))) { /* We're last - so fill forward from last color */ - for (int kk = idx + 1; kk <= k; kk++) { - c[kk] = c[idx]; - } - } - /* Skip - will backfill when we find next color */ - } else if (idx == -1) { /* No previous color, just backfill this color */ - for (int kk = 0; kk < k; kk++) { - c[kk] = c[k]; - } - idx = k; /* This is now last defined color */ - } else { /* Else, interpolate between last idx and this one */ - int cnt = c[k].length; - for (int kk = idx + 1; kk < k; kk++) { - double interp = (double) (kk - idx) / (double) (k - idx); - Color[] cc = new Color[cnt]; - for (int jj = 0; jj < cnt; jj++) { - cc[jj] = new Color((int) ((1.0 - interp) * c[idx][jj].getRed() + interp * c[k][jj].getRed()), - (int) ((1.0 - interp) * c[idx][jj].getGreen() + interp * c[k][jj].getGreen()), - (int) ((1.0 - interp) * c[idx][jj].getBlue() + interp * c[k][jj].getBlue()), - (int) ((1.0 - interp) * c[idx][jj].getAlpha() + interp * c[k][jj].getAlpha())); - } - c[kk] = cc; - } - idx = k; - } - } - } - - public Color[] getRainColor(double rain) { - int idx = (int) (rain * 63.0); - if ((idx >= 0) && (idx < raincolors.length)) - return raincolors[idx]; - else - return null; - } - - public Color[] getTempColor(double temp) { - int idx = (int) (temp * 63.0); - if ((idx >= 0) && (idx < tempcolors.length)) - return tempcolors[idx]; - else - return null; - } - - public static void reset() { - cache.clear(); - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/Component.java b/DynmapCore/src/main/java/org/dynmap/Component.java deleted file mode 100644 index 0d6a47f6..00000000 --- a/DynmapCore/src/main/java/org/dynmap/Component.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.dynmap; - -public abstract class Component { - protected DynmapCore core; - protected ConfigurationNode configuration; - public Component(DynmapCore core, ConfigurationNode configuration) { - this.core = core; - this.configuration = configuration; - } - - public void dispose() { - } - - /* Substitute proper values for escape sequences */ - public static String unescapeString(String v) { - /* Replace color code &color; */ - v = v.replace("&color;", "\u00A7"); - - return v; - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/ComponentManager.java b/DynmapCore/src/main/java/org/dynmap/ComponentManager.java deleted file mode 100644 index c9f10465..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ComponentManager.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.dynmap; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.StreamSupport; - -public class ComponentManager { - public Set components = new HashSet(); - public Map> componentLookup = new HashMap>(); - - public void add(Component c) { - if (components.add(c)) { - String key = c.getClass().toString(); - List clist = componentLookup.get(key); - if (clist == null) { - clist = new ArrayList(); - componentLookup.put(key, clist); - } - clist.add(c); - } - } - - public void remove(Component c) { - if (components.remove(c)) { - String key = c.getClass().toString(); - List clist = componentLookup.get(key); - if (clist != null) { - clist.remove(c); - } - } - } - - public void clear() { - componentLookup.clear(); - components.clear(); - } - - public Iterable getComponents(Class c) { - List list = componentLookup.get(c.toString()); - if (list == null) - return new ArrayList(); - return list; - } - - public Boolean isLoaded(Class c){ - return StreamSupport.stream(getComponents(c).spliterator(), false).count() > 0; - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/ConfigurationNode.java b/DynmapCore/src/main/java/org/dynmap/ConfigurationNode.java deleted file mode 100644 index 2bfb0722..00000000 --- a/DynmapCore/src/main/java/org/dynmap/ConfigurationNode.java +++ /dev/null @@ -1,468 +0,0 @@ -package org.dynmap; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; -import org.yaml.snakeyaml.error.YAMLException; -import org.yaml.snakeyaml.introspector.Property; -import org.yaml.snakeyaml.nodes.CollectionNode; -import org.yaml.snakeyaml.nodes.MappingNode; -import org.yaml.snakeyaml.nodes.Node; -import org.yaml.snakeyaml.nodes.NodeTuple; -import org.yaml.snakeyaml.nodes.SequenceNode; -import org.yaml.snakeyaml.nodes.Tag; -import org.yaml.snakeyaml.reader.UnicodeReader; -import org.yaml.snakeyaml.representer.Represent; -import org.yaml.snakeyaml.representer.Representer; - -public class ConfigurationNode implements Map { - public Map entries; - private File f; - private Yaml yaml; - - public ConfigurationNode() { - entries = new LinkedHashMap(); - } - - private void initparse() { - if(yaml == null) { - DumperOptions options = new DumperOptions(); - - options.setIndent(4); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - options.setPrettyFlow(true); - options.setVersion(DumperOptions.Version.V1_1); - - yaml = new Yaml(new SafeConstructor(), new EmptyNullRepresenter(), options); - } - } - - public ConfigurationNode(File f) { - this.f = f; - entries = new LinkedHashMap(); - } - - public ConfigurationNode(Map map) { - if (map == null) { - throw new IllegalArgumentException(); - } - entries = map; - } - - public ConfigurationNode(InputStream in) { - load(in); - } - - @SuppressWarnings("unchecked") - public boolean load(InputStream in) { - initparse(); - - Object o = yaml.load(new UnicodeReader(in)); - if((o != null) && (o instanceof Map)) - entries = (Map)o; - return (entries != null); - } - - @SuppressWarnings("unchecked") - public boolean load() { - initparse(); - // If no file to read, just return false - if (!f.canRead()) { return false; } - Reader fr = null; - try { - fr = new UnicodeReader(new BufferedInputStream(new FileInputStream(f))); - Object o = yaml.load(fr); - if((o != null) && (o instanceof Map)) - entries = (Map)o; - fr.close(); - } - catch (YAMLException e) { - Log.severe("Error parsing " + f.getPath() + ". Use http://yamllint.com to debug the YAML syntax." ); - throw e; - } catch(IOException iox) { - Log.severe("Error reading " + f.getPath()); - return false; - } finally { - if(fr != null) { - try { fr.close(); } catch (IOException x) {} - } - } - return (entries != null); - } - - public boolean save() { - return save(f); - } - - public boolean save(File file) { - initparse(); - - OutputStream stream = null; - - File parent = file.getParentFile(); - - if (parent != null) { - parent.mkdirs(); - } - - try { - stream = new BufferedOutputStream(new FileOutputStream(file)); - OutputStreamWriter writer = new OutputStreamWriter(stream, "UTF-8"); - yaml.dump(entries, writer); - return true; - } catch (IOException e) { - } finally { - try { - if (stream != null) { - stream.close(); - } - } catch (IOException e) {} - } - return false; - } - - @SuppressWarnings("unchecked") - public Object getObject(String path) { - if (path.isEmpty()) - return entries; - // Try get first (in case '/' is legit part - Object v = get(path); - int separator = path.indexOf('/'); - if ((v != null) || (separator < 0)) return v; - - String localKey = path.substring(0, separator); - Object subvalue = get(localKey); - if (subvalue == null) - return null; - if (!(subvalue instanceof Map)) - return null; - Map submap; - try { - submap = (Map)subvalue; - } catch (ClassCastException e) { - return null; - } - - String subpath = path.substring(separator + 1); - return new ConfigurationNode(submap).getObject(subpath); - - } - - public Object getObject(String path, Object def) { - Object o = getObject(path); - if (o == null) - return def; - return o; - } - - @SuppressWarnings("unchecked") - public T getGeneric(String path, T def) { - Object o = getObject(path, def); - try { - return (T)o; - } catch(ClassCastException e) { - return def; - } - } - - public int getInteger(String path, int def) { - return Integer.parseInt(getObject(path, def).toString()); - } - - public double getLong(String path, long def) { - return Long.parseLong(getObject(path, def).toString()); - } - - public float getFloat(String path, float def) { - return Float.parseFloat(getObject(path, def).toString()); - } - - public double getDouble(String path, double def) { - return Double.parseDouble(getObject(path, def).toString()); - } - - public boolean getBoolean(String path, boolean def) { - return Boolean.parseBoolean(getObject(path, def).toString()); - } - - public String getString(String path) { - return getString(path, null); - } - - public List getStrings(String path, List def) { - Object o = getObject(path); - if (!(o instanceof List)) { - return def; - } - ArrayList strings = new ArrayList(); - for(Object i : (List)o) { - strings.add(i.toString()); - } - return strings; - } - - public String getString(String path, String def) { - Object o = getObject(path, def); - if (o == null) - return null; - return o.toString(); - } - - public Color getColor(String path, String def) { - String lclr = this.getString(path, def); - if((lclr != null) && (lclr.startsWith("#"))) { - try { - int c = Integer.parseInt(lclr.substring(1), 16); - return new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF); - } catch (NumberFormatException nfx) { - Log.severe("Invalid color value: " + lclr + " for '" + path + "'"); - } - } - return null; - } - - @SuppressWarnings("unchecked") - public List getList(String path) { - try { - List list = (List)getObject(path, null); - return list; - } catch (ClassCastException e) { - try { - T o = (T)getObject(path, null); - if (o == null) { - return new ArrayList(); - } - ArrayList al = new ArrayList(); - al.add(o); - return al; - } catch (ClassCastException e2) { - return new ArrayList(); - } - } - } - - public List> getMapList(String path) { - return getList(path); - } - - public ConfigurationNode getNode(String path) { - Map v = null; - v = getGeneric(path, v); - if (v == null) - return null; - return new ConfigurationNode(v); - } - - @SuppressWarnings("unchecked") - public List getNodes(String path) { - List o = getList(path); - - if(o == null) - return new ArrayList(); - - ArrayList nodes = new ArrayList(); - for(Object i : (List)o) { - if (i instanceof Map) { - Map map; - try { - map = (Map)i; - } catch(ClassCastException e) { - continue; - } - nodes.add(new ConfigurationNode(map)); - } - } - return nodes; - } - - public void extend(Map other) { - if (other != null) - extendMap(this, other); - } - - private final static Object copyValue(Object v) { - if(v instanceof Map) { - @SuppressWarnings("unchecked") - Map mv = (Map)v; - LinkedHashMap newv = new LinkedHashMap(); - for(Map.Entry me : mv.entrySet()) { - newv.put(me.getKey(), copyValue(me.getValue())); - } - return newv; - } - else if(v instanceof List) { - @SuppressWarnings("unchecked") - List lv = (List)v; - ArrayList newv = new ArrayList(); - for(int i = 0; i < lv.size(); i++) { - newv.add(copyValue(lv.get(i))); - } - return newv; - } - else { - return v; - } - } - - private final static void extendMap(Map left, Map right) { - ConfigurationNode original = new ConfigurationNode(left); - for(Map.Entry entry : right.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - original.put(key, copyValue(value)); - } - } - - public T createInstance(Class[] constructorParameters, Object[] constructorArguments) { - String typeName = getString("class"); - try { - Class mapTypeClass = Class.forName(typeName); - - Class[] constructorParameterWithConfiguration = new Class[constructorParameters.length+1]; - for(int i = 0; i < constructorParameters.length; i++) { constructorParameterWithConfiguration[i] = constructorParameters[i]; } - constructorParameterWithConfiguration[constructorParameterWithConfiguration.length-1] = ConfigurationNode.class; - - Object[] constructorArgumentsWithConfiguration = new Object[constructorArguments.length+1]; - for(int i = 0; i < constructorArguments.length; i++) { constructorArgumentsWithConfiguration[i] = constructorArguments[i]; } - constructorArgumentsWithConfiguration[constructorArgumentsWithConfiguration.length-1] = this; - Constructor constructor = mapTypeClass.getConstructor(constructorParameterWithConfiguration); - @SuppressWarnings("unchecked") - T t = (T)constructor.newInstance(constructorArgumentsWithConfiguration); - return t; - } catch (Exception e) { - // TODO: Remove reference to MapManager. - Log.severe("Error loading maptype", e); - e.printStackTrace(); - } - return null; - } - - public List createInstances(String path, Class[] constructorParameters, Object[] constructorArguments) { - List nodes = getNodes(path); - List instances = new ArrayList(); - for(ConfigurationNode node : nodes) { - T instance = node.createInstance(constructorParameters, constructorArguments); - if (instance != null) { - instances.add(instance); - } - } - return instances; - } - - @Override - public int size() { - return entries.size(); - } - - @Override - public boolean isEmpty() { - return entries.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return entries.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return entries.containsValue(value); - } - - @Override - public Object get(Object key) { - return entries.get(key); - } - - @Override - public Object put(String key, Object value) { - return entries.put(key, value); - } - - @Override - public Object remove(Object key) { - return entries.remove(key); - } - - @Override - public void putAll(Map m) { - entries.putAll(m); - } - - @Override - public void clear() { - entries.clear(); - } - - @Override - public Set keySet() { - return entries.keySet(); - } - - @Override - public Collection values() { - return entries.values(); - } - - @Override - public Set> entrySet() { - return entries.entrySet(); - } - - private class EmptyNullRepresenter extends Representer { - - public EmptyNullRepresenter() { - super(); - this.nullRepresenter = new EmptyRepresentNull(); - } - - protected class EmptyRepresentNull implements Represent { - public Node representData(Object data) { - return representScalar(Tag.NULL, ""); // Changed "null" to "" so as to avoid writing nulls - } - } - - // Code borrowed from snakeyaml (http://code.google.com/p/snakeyaml/source/browse/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java) - @Override - protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) { - NodeTuple tuple = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); - Node valueNode = tuple.getValueNode(); - if (valueNode instanceof CollectionNode) { - // Removed null check - if (Tag.SEQ.equals(valueNode.getTag())) { - SequenceNode seq = (SequenceNode) valueNode; - if (seq.getValue().isEmpty()) { - return null; // skip empty lists - } - } - if (Tag.MAP.equals(valueNode.getTag())) { - MappingNode seq = (MappingNode) valueNode; - if (seq.getValue().isEmpty()) { - return null; // skip empty maps - } - } - } - return tuple; - } - // End of borrowed code - } - -} diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapChunk.java b/DynmapCore/src/main/java/org/dynmap/DynmapChunk.java deleted file mode 100644 index 62e60148..00000000 --- a/DynmapCore/src/main/java/org/dynmap/DynmapChunk.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.dynmap; - -public class DynmapChunk { - public int x, z; - - public DynmapChunk(int x, int z) { - this.x = x; - this.z = z; - } - @Override - public boolean equals(Object o) { - if(o instanceof DynmapChunk) { - DynmapChunk dc = (DynmapChunk)o; - return (dc.x == this.x) && (dc.z == this.z); - } - return false; - } - @Override - public int hashCode() { - return x ^ (z << 5); - } -} diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java deleted file mode 100644 index bd0e12d5..00000000 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ /dev/null @@ -1,3132 +0,0 @@ -package org.dynmap; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.dynmap.MapType.ImageEncoding; -import org.dynmap.common.DynmapCommandSender; -import org.dynmap.common.DynmapListenerManager; -import org.dynmap.common.DynmapListenerManager.EventType; -import org.dynmap.common.DynmapPlayer; -import org.dynmap.common.DynmapServerInterface; -import org.dynmap.debug.Debug; -import org.dynmap.debug.Debugger; -import org.dynmap.exporter.DynmapExpCommands; -import org.dynmap.hdmap.HDBlockModels; -import org.dynmap.hdmap.HDBlockStateTextureMap; -import org.dynmap.hdmap.TexturePack; -import org.dynmap.markers.MarkerAPI; -import org.dynmap.markers.impl.MarkerAPIImpl; -import org.dynmap.modsupport.ModSupportImpl; -import org.dynmap.renderer.DynmapBlockState; -import org.dynmap.servlet.*; -import org.dynmap.storage.MapStorage; -import org.dynmap.storage.aws_s3.AWSS3MapStorage; -import org.dynmap.storage.filetree.FileTreeMapStorage; -import org.dynmap.storage.mysql.MySQLMapStorage; -import org.dynmap.storage.mssql.MicrosoftSQLMapStorage; -import org.dynmap.storage.mariadb.MariaDBMapStorage; -import org.dynmap.storage.sqllte.SQLiteMapStorage; -import org.dynmap.storage.postgresql.PostgreSQLMapStorage; -import org.dynmap.utils.BlockStep; -import org.dynmap.utils.BufferOutputStream; -import org.dynmap.utils.ImageIOManager; -import org.dynmap.web.BanIPFilter; -import org.dynmap.web.CustomHeaderFilter; -import org.dynmap.web.FileNameFilter; -import org.dynmap.web.FilterHandler; -import org.dynmap.web.HandlerRouter; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.NetworkTrafficServerConnector; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.server.session.DefaultSessionIdManager; -import org.eclipse.jetty.server.session.SessionHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.thread.ExecutorThreadPool; -import org.yaml.snakeyaml.Yaml; - -import javax.servlet.*; -import javax.servlet.http.HttpServlet; - -public class DynmapCore implements DynmapCommonAPI { - /** - * Callbacks for core initialization - subclassed by platform plugins - */ - public static abstract class EnableCoreCallbacks { - /** - * Called during enableCore to report that configuration.txt is loaded - */ - public abstract void configurationLoaded(); - } - private File jarfile; - private DynmapServerInterface server; - private String version; - private String platform = null; - private String platformVersion = null; - private Server webServer = null; - private String webhostname = null; - private int webport = 0; - private HandlerRouter router = null; - public MapManager mapManager = null; - public PlayerList playerList; - public ConfigurationNode configuration; - public ConfigurationNode world_config; - public ComponentManager componentManager = new ComponentManager(); - public DynmapListenerManager listenerManager = new DynmapListenerManager(this); - public PlayerFaces playerfacemgr; - public SkinUrlProvider skinUrlProvider; - public Events events = new Events(); - public String deftemplatesuffix = ""; - private DynmapMapCommands dmapcmds = new DynmapMapCommands(); - private DynmapExpCommands dynmapexpcmds = new DynmapExpCommands(); - boolean bettergrass = false; - boolean smoothlighting = false; - private boolean ctmsupport = false; - private boolean customcolorssupport = false; - private String def_image_format = "png"; - private HashSet enabledTriggers = new HashSet(); - public boolean disable_chat_to_web = false; - private WebAuthManager authmgr; - public boolean player_info_protected; - private boolean transparentLeaves = true; - private List sortPermissionNodes; - private int perTickLimit = 50; // 50 ms - private boolean dumpMissing = false; - private static boolean migrate_chunks = false; - public boolean isInternalWebServerDisabled = false; - - private int config_hashcode; /* Used to signal need to reload web configuration (world changes, config update, etc) */ - private int fullrenderplayerlimit; /* Number of online players that will cause fullrender processing to pause */ - private int updateplayerlimit; /* Number of online players that will cause update processing to pause */ - private String publicURL; // If set, public HRL for accessing dynmap (declared by administrator) - private String noPermissionMsg; - private boolean didfullpause; - private boolean didupdatepause; - private Map> ids_by_ip = new HashMap>(); - private boolean persist_ids_by_ip = false; - private int snapshotcachesize; - private boolean snapshotsoftref; - private String[] biomenames = new String[0]; - private Map blockmap = null; - private Map itemmap = null; - - private boolean loginRequired; - - private String hackAttemptSub = "(IaM5uchA1337Haxr-Ban Me!)"; - // WEBP support - private String cwebpPath; - private String dwebpPath; - private boolean did_cwebpPath_warn = false; - private boolean did_dwebpPath_warn = false; - - /* Flag to let code know that we're doing reload - make sure we don't double-register event handlers */ - public boolean is_reload = false; - public static boolean ignore_chunk_loads = false; /* Flag keep us from processing our own chunk loads */ - - private MarkerAPIImpl markerapi; - - private File dataDirectory; - private File tilesDirectory; - private File exportDirectory; - private File importDirectory; - private String plugin_ver; - private MapStorage defaultStorage; - - // Read web path - private String webpath; - // And whether to disable web file update - private boolean updatewebpathfiles = true; - - private String[] deftriggers = { }; - - private Boolean webserverCompConfigWarn = false; - private final String CompConfigWiki = "https://github.com/webbukkit/dynmap/wiki/Component-Configuration"; - - private final String[] defaultTemplates = {"vlowres", "lowres", "medres", "hires", "low_boost_hi", - "hi_boost_vhi", "hi_boost_xhi"}; - /* Constructor for core */ - public DynmapCore() { - } - - public void setSkinUrlProvider(SkinUrlProvider skinUrlProvider) { - this.skinUrlProvider = skinUrlProvider; - } - - /* Cleanup method */ - public void cleanup() { - server = null; - markerapi = null; - } - public void restartMarkerSaveJob(){ - this.markerapi.scheduleWriteJob(); - } - // Set plugin jar file - public void setPluginJarFile(File f) { - jarfile = f; - } - // Get plugin jar file - public File getPluginJarFile() { - return jarfile; - } - /* Dependencies - need to be supplied by plugin wrapper */ - public void setPluginVersion(String pluginver, String platform) { - this.plugin_ver = pluginver; - this.platform = platform; - } - /* Default platform to forge... */ - public void setPluginVersion(String pluginver) { - setPluginVersion(pluginver, "Forge"); - } - public void setDataFolder(File dir) { - dataDirectory = dir; - } - public final File getDataFolder() { - return dataDirectory; - } - public final File getTilesFolder() { - return tilesDirectory; - } - public final File getExportFolder() { - return exportDirectory; - } - public final File getImportFolder() { - return importDirectory; - } - public void setMinecraftVersion(String mcver) { - this.platformVersion = mcver; - } - public void setServer(DynmapServerInterface srv) { - server = srv; - } - public final DynmapServerInterface getServer() { return server; } - - public final void setBiomeNames(String[] names) { - biomenames = names; - } - - public static final boolean migrateChunks() { - return migrate_chunks; - } - - public String getCWEBPPath() { - if ((cwebpPath == null) && (!did_cwebpPath_warn)) { - Log.severe("ERROR: trying to use WEBP without cwebp tool installed or cwebpPath set properly"); - did_cwebpPath_warn = true; - } - return cwebpPath; - } - public String getDWEBPPath() { - if ((dwebpPath == null) && (!did_dwebpPath_warn)) { - Log.severe("ERROR: trying to use WEBP without dwebp tool installed or dwebpPath set properly"); - did_dwebpPath_warn = true; - } - return dwebpPath; - } - - public final String getBiomeName(int biomeid) { - String n = null; - if ((biomeid >= 0) && (biomeid < biomenames.length)) { - n = biomenames[biomeid]; - } - if(n == null) n = "biome" + biomeid; - return n; - } - public final String[] getBiomeNames() { - return biomenames; - } - - public final MapManager getMapManager() { - return mapManager; - } - - public final void setTriggerDefault(String[] triggers) { - deftriggers = triggers; - } - - public final void setLeafTransparency(boolean trans) { - transparentLeaves = trans; - } - public final boolean getLeafTransparency() { - return transparentLeaves; - } - - /* Add/Replace branches in configuration tree with contribution from a separate file */ - private void mergeConfigurationBranch(ConfigurationNode cfgnode, String branch, boolean replace_existing, boolean islist) { - Object srcbranch = cfgnode.getObject(branch); - if(srcbranch == null) - return; - /* See if top branch is in configuration - if not, just add whole thing */ - Object destbranch = configuration.getObject(branch); - if(destbranch == null) { /* Not found */ - configuration.put(branch, srcbranch); /* Add new tree to configuration */ - return; - } - /* If list, merge by "name" attribute */ - if(islist) { - List dest = configuration.getNodes(branch); - List src = cfgnode.getNodes(branch); - /* Go through new records : see what to do with each */ - for(ConfigurationNode node : src) { - String name = node.getString("name", null); - if(name == null) continue; - /* Walk destination - see if match */ - boolean matched = false; - for(ConfigurationNode dnode : dest) { - String dname = dnode.getString("name", null); - if(dname == null) continue; - if(dname.equals(name)) { /* Match? */ - if(replace_existing) { - dnode.clear(); - dnode.putAll(node); - } - matched = true; - break; - } - } - /* If no match, add to end */ - if(!matched) { - dest.add(node); - } - } - configuration.put(branch,dest); - } - /* If configuration node, merge by key */ - else { - ConfigurationNode src = cfgnode.getNode(branch); - ConfigurationNode dest = configuration.getNode(branch); - for(String key : src.keySet()) { /* Check each contribution */ - if(dest.containsKey(key)) { /* Exists? */ - if(replace_existing) { /* If replacing, do so */ - dest.put(key, src.getObject(key)); - } - } - else { /* Else, always add if not there */ - dest.put(key, src.getObject(key)); - } - } - } - } - /* Table of default templates - all are resources in dynmap.jar unnder templates/, and go in templates directory when needed */ - private static final String[] stdtemplates = { "normal.txt", "nether.txt", "normal-lowres.txt", - "nether-lowres.txt", "normal-hires.txt", "nether-hires.txt", - "normal-vlowres.txt", "nether-vlowres.txt", "the_end.txt", "the_end-vlowres.txt", - "the_end-lowres.txt", "the_end-hires.txt", - "normal-low_boost_hi.txt", "normal-hi_boost_vhi.txt", "normal-hi_boost_xhi.txt", - "nether-low_boost_hi.txt", "nether-hi_boost_vhi.txt", "nether-hi_boost_xhi.txt", - "the_end-low_boost_hi.txt", "the_end-hi_boost_vhi.txt", "the_end-hi_boost_xhi.txt" - }; - - private static final String CUSTOM_PREFIX = "custom-"; - /* Load templates from template folder */ - private void loadTemplates() { - File templatedir = new File(dataDirectory, "templates"); - templatedir.mkdirs(); - /* First, prime the templates directory with default standard templates, if needed */ - for(String stdtemplate : stdtemplates) { - File f = new File(templatedir, stdtemplate); - updateVersionUsingDefaultResource("/templates/" + stdtemplate, f); - } - /* Now process files */ - String[] templates = templatedir.list(); - /* Go through list - process all ones not starting with 'custom' first */ - for(String tname: templates) { - /* If matches naming convention */ - if(tname.endsWith(".txt") && (!tname.startsWith(CUSTOM_PREFIX))) { - File tf = new File(templatedir, tname); - ConfigurationNode cn = new ConfigurationNode(tf); - cn.load(); - /* Supplement existing values (don't replace), since configuration.txt is more custom than these */ - mergeConfigurationBranch(cn, "templates", false, false); - } - } - /* Go through list again - this time do custom- ones */ - for(String tname: templates) { - /* If matches naming convention */ - if(tname.endsWith(".txt") && tname.startsWith(CUSTOM_PREFIX)) { - File tf = new File(templatedir, tname); - ConfigurationNode cn = new ConfigurationNode(tf); - cn.load(); - /* This are overrides - replace even configuration.txt content */ - mergeConfigurationBranch(cn, "templates", true, false); - } - } - } - - public boolean enableCore() { - boolean rslt = initConfiguration(null); - if (rslt) - rslt = enableCore(null); - return rslt; - } - - public boolean initConfiguration(EnableCoreCallbacks cb) { - /* Start with clean events */ - events = new Events(); - /* Default to being unprotected - set to protected by update components */ - player_info_protected = false; - - /* Load plugin version info */ - loadVersion(); - - /* Initialize confguration.txt if needed */ - File f = new File(dataDirectory, "configuration.txt"); - if(!createDefaultFileFromResource("/configuration.txt", f)) { - return false; - } - - /* Load configuration.txt */ - configuration = new ConfigurationNode(f); - configuration.load(); - - // Read web path - webpath = configuration.getString("webpath", "web"); - // And whether to disable web file update - updatewebpathfiles = configuration.getBoolean("update-webpath-files", true); - - // Check if we are disabling the internal web server (implies external) - isInternalWebServerDisabled = configuration.getBoolean("disable-webserver", false); - - /* Prime the tiles directory */ - tilesDirectory = getFile(configuration.getString("tilespath", "web/tiles")); - if (!tilesDirectory.isDirectory() && !tilesDirectory.mkdirs()) { - Log.warning("Could not create directory for tiles ('" + tilesDirectory + "')."); - } - // Prime the exports directory - exportDirectory = getFile(configuration.getString("exportpath", "export")); - if (!exportDirectory.isDirectory() && !exportDirectory.mkdirs()) { - Log.warning("Could not create directory for exports ('" + exportDirectory + "')."); - } - // Prime the imports directory - importDirectory = getFile(configuration.getString("importpath", "import")); - if (!importDirectory.isDirectory() && !importDirectory.mkdirs()) { - Log.warning("Could not create directory for imports ('" + importDirectory + "')."); - } - // Create default storage handler - String storetype = configuration.getString("storage/type", "filetree"); - if (storetype.equals("filetree")) { - defaultStorage = new FileTreeMapStorage(); - } - else if (storetype.equals("sqlite")) { - defaultStorage = new SQLiteMapStorage(); - } - else if (storetype.equals("mysql")) { - defaultStorage = new MySQLMapStorage(); - } - else if (storetype.equals("mariadb")) { - defaultStorage = new MariaDBMapStorage(); - } - else if (storetype.equals("postgres") || storetype.equals("postgresql")) { - defaultStorage = new PostgreSQLMapStorage(); - } - else if (storetype.equals("aws_s3")) { - defaultStorage = new AWSS3MapStorage(); - } - else if (storetype.equals("microsoftsql")) { - defaultStorage = new MicrosoftSQLMapStorage(); - } - else { - Log.severe("Invalid storage type for map data: " + storetype); - return false; - } - if (!defaultStorage.init(this)) { - Log.severe("Map storage initialization failure"); - return false; - } - - /* Register API with plugin, if needed */ - if(!markerAPIInitialized()) { - MarkerAPIImpl api = MarkerAPIImpl.initializeMarkerAPI(this); - this.registerMarkerAPI(api); - } - /* Call back to plugin to report that configuration is available */ - if(cb != null) - cb.configurationLoaded(); - return true; - } - - private String findExecutableOnPath(String fname) { - String path = System.getenv("PATH"); - // Fast-fail if path is null. - if (path == null) - return null; - - for (String dirname : path.split(File.pathSeparator)) { - File file = new File(dirname, fname); - if (file.isFile() && file.canExecute()) { - return file.getAbsolutePath(); - } - file = new File(dirname, fname + ".exe"); - if (file.isFile() && file.canExecute()) { - return file.getAbsolutePath(); - } - } - return null; - } - - public boolean enableCore(EnableCoreCallbacks cb) { - /* Update extracted files, if needed */ - updateExtractedFiles(); - /* Initialize authorization manager */ - if(configuration.getBoolean("login-enabled", false)) { - authmgr = new WebAuthManager(this); - defaultStorage.setLoginEnabled(this); - } - // If storage serves web files, extract and publsh them - if (defaultStorage.needsStaticWebFiles()) { - updateStaticWebToStorage(); - } - /* Load control for leaf transparency (spout lighting bug workaround) */ - transparentLeaves = configuration.getBoolean("transparent-leaves", true); - - // Inject core instance - ImageIOManager.core = this; - // Check for webp support - cwebpPath = configuration.getString("cwebpPath", null); - dwebpPath = configuration.getString("dwebpPath", null); - if (cwebpPath == null) { - cwebpPath = findExecutableOnPath("cwebp"); - } - if (dwebpPath == null) { - dwebpPath = findExecutableOnPath("dwebp"); - } - if (cwebpPath != null) { - File file = new File(cwebpPath); - if (!file.isFile() || !file.canExecute()) { - cwebpPath = null; - } - } - if (dwebpPath != null) { - File file = new File(dwebpPath); - if (!file.isFile() || !file.canExecute()) { - dwebpPath = null; - } - } - if ((cwebpPath != null) && (dwebpPath != null)) { - Log.info("Found cwebp at " + cwebpPath + " and dwebp at " + dwebpPath + ": webp format enabled"); - } - else { - cwebpPath = dwebpPath = null; - } - /* Get default image format */ - def_image_format = configuration.getString("image-format", "png"); - MapType.ImageFormat fmt = MapType.ImageFormat.fromID(def_image_format); - if ((fmt == null) || ((fmt.enc == ImageEncoding.WEBP) && (cwebpPath == null))) { - Log.severe("Invalid image-format: " + def_image_format); - def_image_format = "png"; - fmt = MapType.ImageFormat.fromID(def_image_format); - } - - - DynmapWorld.doInitialScan(configuration.getBoolean("initial-zoomout-validate", true)); - - smoothlighting = configuration.getBoolean("smooth-lighting", false); - ctmsupport = configuration.getBoolean("ctm-support", true); - customcolorssupport = configuration.getBoolean("custom-colors-support", true); - Log.verbose = configuration.getBoolean("verbose", true); - deftemplatesuffix = configuration.getString("deftemplatesuffix", ""); - /* Get snapshot cache size */ - snapshotcachesize = configuration.getInteger("snapshotcachesize", 500); - /* Get soft ref flag for cache (weak=false, soft=true) */ - snapshotsoftref = configuration.getBoolean("soft-ref-cache", true); - /* Default better-grass */ - bettergrass = configuration.getBoolean("better-grass", false); - /* Load full render processing player limit */ - fullrenderplayerlimit = configuration.getInteger("fullrenderplayerlimit", 0); - /* Load update render processing player limit */ - updateplayerlimit = configuration.getInteger("updateplayerlimit", 0); - /* Load sort permission nodes */ - sortPermissionNodes = configuration.getStrings("player-sort-permission-nodes", null); - - perTickLimit = configuration.getInteger("per-tick-time-limit", 50); - if (perTickLimit < 5) perTickLimit = 5; - - dumpMissing = configuration.getBoolean("dump-missing-blocks", false); - - migrate_chunks = configuration.getBoolean("migrate-chunks", false); - if (migrate_chunks) - Log.info("EXPERIMENTAL: chunk migration enabled"); - - publicURL = configuration.getString("publicURL", ""); - - /* Send this message if the player does not have permission to use the command */ - noPermissionMsg = configuration.getString("noPermissionMsg", "You don't have permission to use this command!"); - - /* Load preupdate/postupdate commands */ - ImageIOManager.preUpdateCommand = configuration.getString("custom-commands/image-updates/preupdatecommand", ""); - ImageIOManager.postUpdateCommand = configuration.getString("custom-commands/image-updates/postupdatecommand", ""); - - /* Get block and item maps */ - blockmap = server.getBlockUniqueIDMap(); - itemmap = server.getItemUniqueIDMap(); - - /* Process mod support */ - ModSupportImpl.complete(this.dataDirectory); - // Finalize block state - DynmapBlockState.finalizeBlockStates(); - /* Load block models */ - Log.verboseinfo("Loading models..."); - HDBlockModels.loadModels(this, configuration); - /* Load texture mappings */ - Log.verboseinfo("Loading texture mappings..."); - TexturePack.loadTextureMapping(this, configuration); - - /* Now, process worlds.txt - merge it in as an override of existing values (since it is only user supplied values) */ - File f = new File(dataDirectory, "worlds.txt"); - if(!createDefaultFileFromResource("/worlds.txt", f)) { - return false; - } - world_config = new ConfigurationNode(f); - world_config.load(); - - /* Now, process templates */ - Log.verboseinfo("Loading templates..."); - loadTemplates(); - - /* If we're persisting ids-by-ip, load it */ - persist_ids_by_ip = configuration.getBoolean("persist-ids-by-ip", true); - if(persist_ids_by_ip) { - Log.verboseinfo("Loading userid-by-IP data..."); - loadIDsByIP(); - } - - loadDebuggers(); - - playerList = new PlayerList(getServer(), getFile("hiddenplayers.txt"), configuration); - playerList.load(); - - mapManager = new MapManager(this, configuration); - mapManager.startRendering(); - - if (markerapi != null) { - MarkerAPIImpl.completeInitializeMarkerAPI(markerapi); - } - - playerfacemgr = new PlayerFaces(this); - - updateConfigHashcode(); /* Initialize/update config hashcode */ - - loginRequired = configuration.getBoolean("login-required", false); - hackAttemptSub = configuration.getString("hackAttemptBlurb", "(IaM5uchA1337Haxr-Ban Me!)"); - - // If not disabled, load and initialize the internal web server - if (!isInternalWebServerDisabled) { - loadWebserver(); - } - - - enabledTriggers.clear(); - List triggers = configuration.getStrings("render-triggers", new ArrayList()); - if ((triggers != null) && (triggers.size() > 0)) - { - for (Object trigger : triggers) { - enabledTriggers.add((String) trigger); - } - } - else { - for (String def : deftriggers) { - enabledTriggers.add(def); - } - } - - // Load components. - for(Component component : configuration.createInstances("components", new Class[] { DynmapCore.class }, new Object[] { this })) { - componentManager.add(component); - } - Log.verboseinfo("Loaded " + componentManager.components.size() + " components."); - - if (!isInternalWebServerDisabled) { // If internal not disabled, we should be using it and not external - startWebserver(); - if (!componentManager.isLoaded(InternalClientUpdateComponent.class)) { - Log.warning("Using internal server, but " + InternalClientUpdateComponent.class.toString() + " is DISABLED!"); - webserverCompConfigWarn = true; - } - if (componentManager.isLoaded(JsonFileClientUpdateComponent.class)) { - Log.warning("Using internal server, but " + JsonFileClientUpdateComponent.class.toString() + " is ENABLED!"); - } - } - else { - if (componentManager.isLoaded(InternalClientUpdateComponent.class)) { - Log.warning("Using external server, but " + InternalClientUpdateComponent.class.toString() + " is ENABLED!"); - } - if (!componentManager.isLoaded(JsonFileClientUpdateComponent.class)) { - Log.warning("Using external server, but " + JsonFileClientUpdateComponent.class.toString() + " is DISABLED!"); - webserverCompConfigWarn = true; - } - } - if (webserverCompConfigWarn) { - Log.warning("If the website is missing files or not loading/updating, this might be why."); - Log.warning("For more info, read this: " + CompConfigWiki); - webserverCompConfigWarn = false; - } - - /* Add login/logoff listeners */ - listenerManager.addListener(EventType.PLAYER_JOIN, new DynmapListenerManager.PlayerEventListener() { - @Override - public void playerEvent(DynmapPlayer p) { - playerJoined(p); - } - }); - listenerManager.addListener(EventType.PLAYER_QUIT, new DynmapListenerManager.PlayerEventListener() { - @Override - public void playerEvent(DynmapPlayer p) { - playerQuit(p); - } - }); - - /* Print version info */ - Log.info("version " + plugin_ver + " is enabled - core version " + version ); - Log.info("For support, visit our Discord at https://discord.gg/s3rd5qn"); - Log.info("For news, visit https://reddit.com/r/Dynmap or follow https://universeodon.com/@dynmap"); - Log.info("To report or track bugs, visit https://github.com/webbukkit/dynmap/issues"); - Log.info("If you'd like to donate, please visit https://www.patreon.com/dynmap or https://ko-fi.com/michaelprimm"); - - events.trigger("initialized", null); - - if (configuration.getBoolean("dumpColorMaps", false)) { - dumpColorMap("standard.txt", "standard"); - dumpColorMap("default.txt", "standard"); - dumpColorMap("dokudark.txt", "dokudark.zip"); - dumpColorMap("dokulight.txt", "dokulight.zip"); - dumpColorMap("dokuhigh.txt", "dokuhigh.zip"); - dumpColorMap("misa.txt", "misa.zip"); - dumpColorMap("sphax.txt", "sphax.zip"); - dumpColorMap("ovocean.txt", "ovocean.zip"); - dumpColorMap("flames.txt", "standard"); // No TP around for this - dumpColorMap("sk89q.txt", "standard"); // No TP around for this - dumpColorMap("amidst.txt", "standard"); // No TP around for this - } - - if (configuration.getBoolean("dumpBlockState", false)) { - Log.info("Block State Dump"); - Log.info("----------------"); - for (int i = 0; i < DynmapBlockState.getGlobalIndexMax(); i++) { - DynmapBlockState bs = DynmapBlockState.getStateByGlobalIndex(i); - if (bs != null) { - Log.info(String.format("%d: %s (index %d)", i, bs.toString(), bs.stateIndex)); - } - } - Log.info("----------------"); - } - if (configuration.getBoolean("dumpBlockNames", false)) { - Log.info("Block Name dump"); - Log.info("---------------"); - for (int i = 0; i < DynmapBlockState.getGlobalIndexMax(); ) { - DynmapBlockState bs = DynmapBlockState.getStateByGlobalIndex(i); - if (bs != null) { - Log.info(String.format("%d,%s,%d", i, bs.blockName, bs.getStateCount())); - i += bs.getStateCount(); - } - else { - i++; - } - } - Log.info("---------------"); - } - return true; - } - - void dumpColorMap(String id, String name) { - int[] sides = new int[] { BlockStep.Y_MINUS.ordinal(), BlockStep.X_PLUS.ordinal(), BlockStep.Z_PLUS.ordinal(), - BlockStep.Y_PLUS.ordinal(), BlockStep.X_MINUS.ordinal(), BlockStep.Z_MINUS.ordinal() }; - FileWriter fw = null; - try { - fw = new FileWriter(new File(new File(getDataFolder(), "colorschemes"), id)); - TexturePack tp = TexturePack.getTexturePack(this, name); - if (tp == null) return; - tp = tp.resampleTexturePack(1); - if (tp == null) return; - Color c = new Color(); - for (int gidx = 0; gidx < DynmapBlockState.getGlobalIndexMax(); gidx++) { - DynmapBlockState blk = DynmapBlockState.getStateByGlobalIndex(gidx); - if (blk.isAir()) continue; - int meta0color = 0; - HDBlockStateTextureMap map = HDBlockStateTextureMap.getByBlockState(blk); - boolean done = false; - for (int i = 0; (!done) && (i < sides.length); i++) { - int idx = map.getIndexForFace(sides[i]); - if (idx < 0) continue; - int rgb[] = tp.getTileARGB(idx % 1000000); - if (rgb == null) continue; - if (rgb[0] == 0) continue; - c.setARGB(rgb[0]); - idx = (idx / 1000000); - switch(idx) { - case 1: // grass - case 18: // grass - Log.verboseinfo("Used grass for " + blk); - c.blendColor(tp.getTrivialGrassMultiplier() | 0xFF000000); - break; - case 2: // foliage - case 19: // foliage - case 22: // foliage - Log.verboseinfo("Used foliage for " + blk); - c.blendColor(tp.getTrivialFoliageMultiplier() | 0xFF000000); - break; - case 13: // pine - c.blendColor(0x619961 | 0xFF000000); - break; - case 14: // birch - c.blendColor(0x80a755 | 0xFF000000); - break; - case 15: // lily - c.blendColor(0x208030 | 0xFF000000); - break; - case 3: // water - case 20: // water - Log.verboseinfo("Used water for " + blk); - c.blendColor(tp.getTrivialWaterMultiplier() | 0xFF000000); - break; - case 12: // clear inside - if (blk.isWater()) { // special case for water - Log.verboseinfo("Used water for " + blk); - c.blendColor(tp.getTrivialWaterMultiplier() | 0xFF000000); - } - break; - } - int custmult = tp.getCustomBlockMultiplier(blk); - if (custmult != 0xFFFFFF) { - Log.info(String.format("Custom color: %06x for %s", custmult, blk)); - if ((custmult & 0xFF000000) == 0) { - custmult |= 0xFF000000; - } - c.blendColor(custmult); - } - String ln = ""; - if (blk.stateIndex == 0) { - meta0color = c.getARGB(); - ln = blk.blockName + " "; - } - else { - ln = blk + " "; - } - if ((blk.stateIndex == 0) || (meta0color != c.getARGB())) { - ln += c.getRed() + " " + c.getGreen() + " " + c.getBlue() + " " + c.getAlpha(); - ln += " " + (c.getRed()*4/5) + " " + (c.getGreen()*4/5) + " " + (c.getBlue()*4/5) + " " + c.getAlpha(); - ln += " " + (c.getRed()/2) + " " + (c.getGreen()/2) + " " + (c.getBlue()/2) + " " + c.getAlpha(); - ln += " " + (c.getRed()*2/5) + " " + (c.getGreen()*2/5) + " " + (c.getBlue()*2/5) + " " + c.getAlpha() + "\n"; - fw.write(ln); - } - done = true; - } - } - } catch (IOException iox) { - } finally { - if (fw != null) { try { fw.close(); } catch (IOException x) {} } - Log.info("Dumped RP=" + name + " to " + id); - } - } - - private void playerJoined(DynmapPlayer p) { - playerList.updateOnlinePlayers(null); - if((fullrenderplayerlimit > 0) || (updateplayerlimit > 0)) { - int pcnt = getServer().getOnlinePlayers().length; - - if ((fullrenderplayerlimit > 0) && (pcnt == fullrenderplayerlimit)) { - if(getPauseFullRadiusRenders() == false) { /* If not paused, pause it */ - setPauseFullRadiusRenders(true); - Log.info("Pause full/radius renders - player limit reached"); - didfullpause = true; - } - else { - didfullpause = false; - } - } - if ((updateplayerlimit > 0) && (pcnt == updateplayerlimit)) { - if(getPauseUpdateRenders() == false) { /* If not paused, pause it */ - setPauseUpdateRenders(true); - Log.info("Pause tile update renders - player limit reached"); - didupdatepause = true; - } - else { - didupdatepause = false; - } - } - } - /* Add player info to IP-to-ID table */ - InetSocketAddress addr = p.getAddress(); - if(addr != null) { - String ip = addr.getAddress().getHostAddress(); - LinkedList ids = ids_by_ip.get(ip); - if(ids == null) { - ids = new LinkedList(); - ids_by_ip.put(ip, ids); - } - String pid = p.getName(); - if(ids.indexOf(pid) != 0) { - ids.remove(pid); /* Remove from list */ - ids.addFirst(pid); /* Put us first on list */ - } - } - /* Check sort weight permissions list */ - if ((sortPermissionNodes != null) && (sortPermissionNodes.size() > 0)) { - int ord; - for (ord = 0; ord < sortPermissionNodes.size(); ord++) { - if (p.hasPermissionNode(sortPermissionNodes.get(ord))) { - break; - } - } - p.setSortWeight(ord); - } - else { - p.setSortWeight(0); // Initialize to zero - } - /* And re-attach to active jobs */ - if(mapManager != null) - mapManager.connectTasksToPlayer(p); - } - - /* Called by plugin each time a player quits the server */ - private void playerQuit(DynmapPlayer p) { - playerList.updateOnlinePlayers(p.getName()); - if ((fullrenderplayerlimit > 0) || (updateplayerlimit > 0)) { - /* Quitting player is still online at this moment, so discount count by 1 */ - int pcnt = getServer().getOnlinePlayers().length - 1; - if ((fullrenderplayerlimit > 0) && (pcnt == (fullrenderplayerlimit - 1))) { - if(didfullpause && getPauseFullRadiusRenders()) { /* Only unpause if we did the pause */ - setPauseFullRadiusRenders(false); - Log.info("Resume full/radius renders - below player limit"); - } - didfullpause = false; - } - if ((updateplayerlimit > 0) && (pcnt == (updateplayerlimit - 1))) { - if(didupdatepause && getPauseUpdateRenders()) { /* Only unpause if we did the pause */ - setPauseUpdateRenders(false); - Log.info("Resume tile update renders - below player limit"); - } - didupdatepause = false; - } - } - } - - public void updateConfigHashcode() { - config_hashcode = (int)System.currentTimeMillis(); - } - - public int getConfigHashcode() { - return config_hashcode; - } - - @SuppressWarnings("deprecation") - private org.eclipse.jetty.util.resource.FileResource createFileResource(String path) { - try { - File f = new File(path); - URI uri = f.toURI(); - URL url = uri.toURL(); - return new org.eclipse.jetty.util.resource.FileResource(url); - } catch(Exception e) { - Log.info("Could not create file resource"); - return null; - } - } - - public void loadWebserver() { - org.eclipse.jetty.util.log.Log.setLog(new JettyNullLogger()); - String ip = server.getServerIP(); - if ((ip == null) || (ip.trim().length() == 0)) { - ip = "0.0.0.0"; - } - webhostname = configuration.getString("webserver-bindaddress", ip); - webport = configuration.getInteger("webserver-port", 8123); - - int maxconnections = configuration.getInteger("max-sessions", 30); - if(maxconnections < 2) maxconnections = 2; - LinkedBlockingQueue queue = new LinkedBlockingQueue(maxconnections); - ExecutorThreadPool pool = new ExecutorThreadPool(maxconnections, 2, queue); - - webServer = new Server(pool); - webServer.setSessionIdManager(new DefaultSessionIdManager(webServer)); - - NetworkTrafficServerConnector connector = new NetworkTrafficServerConnector(webServer); - connector.setIdleTimeout(5000); - connector.setAcceptQueueSize(50); - if(webhostname.equals("0.0.0.0") == false) - connector.setHost(webhostname); - connector.setPort(webport); - webServer.setConnectors(new Connector[]{connector}); - - webServer.setStopAtShutdown(true); - //webServer.setGracefulShutdown(1000); - final boolean allow_symlinks = configuration.getBoolean("allow-symlinks", false); - router = new HandlerRouter() {{ - FileResourceHandler fileResourceHandler = new FileResourceHandler() {{ - this.setWelcomeFiles(new String[] { "index.html" }); - this.setRedirectWelcome(false); - this.setDirectoriesListed(true); - this.setBaseResource(createFileResource(getFile(getWebPath()).getAbsolutePath())); - }}; - try { - fileResourceHandler.doStart(); - }catch (Exception ex){ - ex.printStackTrace(); - Log.severe("Failed to start resource handler: "+ex.getMessage()); - } - ContextHandler fileResourceContext = new ContextHandler(); - fileResourceContext.setHandler(fileResourceHandler); - fileResourceContext.clearAliasChecks(); - if (allow_symlinks){ - fileResourceContext.addAliasCheck(new ContextHandler.ApproveAliases()); - fileResourceContext.addAliasCheck(new ContextHandler.ApproveNonExistentDirectoryAliases()); - fileResourceContext.addAliasCheck(new AllowSymLinkAliasChecker()); - } - try { - Class handlerClass = fileResourceHandler.getClass().getSuperclass().getSuperclass(); - Field field = handlerClass.getDeclaredField("_context"); - field.setAccessible(true); - field.set(fileResourceHandler,fileResourceContext); - }catch (Exception e){ - Log.severe("Failed to initialize resource handler: "+e.getMessage()); - } - this.addHandler("/", fileResourceHandler); - this.addHandler("/tiles/*", new MapStorageResourceHandler() {{ - this.setCore(DynmapCore.this); - }}); - }}; - - if(allow_symlinks) - Log.verboseinfo("Web server is permitting symbolic links"); - else - Log.verboseinfo("Web server is not permitting symbolic links"); - - List filters = new LinkedList(); - - /* Check for banned IPs */ - boolean checkbannedips = configuration.getBoolean("check-banned-ips", true); - if (checkbannedips) { - filters.add(new BanIPFilter(this)); - } - filters.add(new FileNameFilter(this)); - -// filters.add(new LoginFilter(this)); - - /* Load customized response headers, if any */ - filters.add(new CustomHeaderFilter(configuration.getNode("http-response-headers"))); - - FilterHandler fh = new FilterHandler(router, filters); - ContextHandler contextHandler = new ContextHandler(); - contextHandler.setContextPath("/"); - contextHandler.setHandler(fh); - HandlerList hlist = new HandlerList(); - hlist.setHandlers(new org.eclipse.jetty.server.Handler[] { new SessionHandler(), contextHandler }); - webServer.setHandler(hlist); - - addServlet("/up/configuration", new ClientConfigurationServlet(this)); - addServlet("/standalone/config.js", new ConfigJSServlet(this)); - if(authmgr != null) { - LoginServlet login = new LoginServlet(this); - addServlet("/up/login", login); - addServlet("/up/register", login); - } - } - - public boolean isLoginSupportEnabled() { - return (authmgr != null); - } - - public boolean isLoginRequired() { - return loginRequired; - } - - public boolean isCTMSupportEnabled() { - return ctmsupport; - } - - public boolean isCustomColorsSupportEnabled() { - return customcolorssupport; - } - - public Set getIPBans() { - return getServer().getIPBans(); - } - - public void addServlet(String path, HttpServlet servlet) { - new ServletHolder(servlet); - router.addServlet(path, servlet); - } - - - public void startWebserver() { - try { - if(webServer != null) { - webServer.start(); - Log.info("Web server started on address " + webhostname + ":" + webport); - } - } catch (Exception e) { - Log.severe("Failed to start WebServer on address " + webhostname + ":" + webport + " : " + e.getMessage()); - } - } - - public void disableCore() { - if(persist_ids_by_ip) - saveIDsByIP(); - - if (webServer != null) { - try { - webServer.stop(); - for(int i = 0; i < 100; i++) { /* Limit wait to 10 seconds */ - if(webServer.isStopping()) - Thread.sleep(100); - } - if(webServer.isStopping()) { - Log.warning("Graceful shutdown timed out - continuing to terminate"); - } - } catch (Exception e) { - Log.severe("Failed to stop WebServer!", e); - } - webServer = null; - } - - if (componentManager != null) { - int componentCount = componentManager.components.size(); - for(Component component : componentManager.components) { - component.dispose(); - } - componentManager.clear(); - Log.info("Unloaded " + componentCount + " components."); - } - - if (mapManager != null) { - mapManager.stopRendering(); - mapManager = null; - } - if (defaultStorage != null) { - defaultStorage.shutdownStorage(); - } - playerfacemgr = null; - /* Clean up registered listeners */ - listenerManager.cleanup(); - - /* Don't clean up markerAPI - other plugins may still be accessing it */ - - authmgr = null; - - Debug.clearDebuggers(); - } - - private static File combinePaths(File parent, String path) { - return combinePaths(parent, new File(path)); - } - - private static File combinePaths(File parent, File path) { - if (path.isAbsolute()) - return path; - return new File(parent, path.getPath()); - } - - public File getFile(String path) { - return combinePaths(getDataFolder(), path); - } - - protected void loadDebuggers() { - List debuggersConfiguration = configuration.getNodes("debuggers"); - Debug.clearDebuggers(); - for (ConfigurationNode debuggerConfiguration : debuggersConfiguration) { - try { - Class debuggerClass = Class.forName((String) debuggerConfiguration.getString("class")); - Constructor constructor = debuggerClass.getConstructor(DynmapCore.class, ConfigurationNode.class); - Debugger debugger = (Debugger) constructor.newInstance(this, debuggerConfiguration); - Debug.addDebugger(debugger); - } catch (Exception e) { - Log.severe("Error loading debugger: " + e); - e.printStackTrace(); - continue; - } - } - } - - /* Parse argument strings : handle quoted strings */ - public static String[] parseArgs(String[] args, DynmapCommandSender snd) { - return parseArgs(args, snd, false); - } - - /* Parse argument strings : handle quoted strings */ - public static String[] parseArgs(String[] args, DynmapCommandSender snd, boolean allowUnclosedQuotes) { - ArrayList rslt = new ArrayList(); - /* Build command line, so we can parse our way - make sure there is trailing space */ - String cmdline = ""; - for(int i = 0; i < args.length; i++) { - cmdline += args[i] + " "; - } - boolean inquote = false; - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < cmdline.length(); i++) { - char c = cmdline.charAt(i); - if(inquote) { /* If in quote, accumulate until end or another quote */ - if(c == '\"') { /* End quote */ - inquote = false; - } - else { - sb.append(c); - } - } - else if(c == '\"') { /* Start of quote? */ - inquote = true; - } - else if(c == ' ') { /* Ending space? */ - rslt.add(sb.toString()); - sb.setLength(0); - } - else { - sb.append(c); - } - } - if(inquote) { // If still in quote - if(allowUnclosedQuotes) { - if(sb.length() > 1) { // Add remaining input without trailing space - rslt.add(sb.substring(0, sb.length() - 1)); - } - } else { // Syntax error - snd.sendMessage("Error: unclosed doublequote"); - return null; - } - } - return rslt.toArray(new String[rslt.size()]); - } - - private static final Set commands = new HashSet(Arrays.asList(new String[] { - "render", - "hide", - "show", - "version", - "fullrender", - "cancelrender", - "radiusrender", - "updaterender", - "stats", - "triggerstats", - "resetstats", - "sendtoweb", - "pause", - "purgequeue", - "purgemap", - "purgeworld", - "quiet", - "ids-for-ip", - "ips-for-id", - "add-id-for-ip", - "del-id-for-ip", - "webregister", - "dumpmemory", - "url", - "help"})); - - private static class CommandInfo { - final String cmd; - final String subcmd; - final String args; - final String helptext; - public CommandInfo(String cmd, String subcmd, String helptxt) { - this.cmd = cmd; - this.subcmd = subcmd; - this.helptext = helptxt; - this.args = ""; - } - public CommandInfo(String cmd, String subcmd, String args, String helptxt) { - this.cmd = cmd; - this.subcmd = subcmd; - this.args = args; - this.helptext = helptxt; - } - public boolean matches(String c, String sc) { - return (cmd.equals(c) && subcmd.equals(sc)); - } - public boolean matches(String c) { - return cmd.equals(c); - } - }; - - private static final CommandInfo[] commandinfo = { - new CommandInfo("dynmap", "", "Control execution of dynmap."), - new CommandInfo("dynmap", "hide", "Hides the current player from the map."), - new CommandInfo("dynmap", "hide", "", "Hides on the map."), - new CommandInfo("dynmap", "show", "Shows the current player on the map."), - new CommandInfo("dynmap", "show", "", "Shows on the map."), - new CommandInfo("dynmap", "render", "Renders the tile at your location."), - new CommandInfo("dynmap", "fullrender", "Render all maps for entire world from your location."), - new CommandInfo("dynmap", "fullrender", "", "Render all maps for world ."), - new CommandInfo("dynmap", "fullrender", ":", "Render map of world ."), - new CommandInfo("dynmap", "fullrender", "resume ", "Resume render of all maps for world . Skip already rendered tiles."), - new CommandInfo("dynmap", "fullrender", "resume :", "Resume render of map of world . Skip already rendered tiles."), - new CommandInfo("dynmap", "radiusrender", "", "Render at least block radius from your location on all maps."), - new CommandInfo("dynmap", "radiusrender", " ", "Render at least block radius from your location on map ."), - new CommandInfo("dynmap", "radiusrender", " ", "Render at least block radius from location , on world ."), - new CommandInfo("dynmap", "radiusrender", " ", "Render at least block radius from location , on world on map ."), - new CommandInfo("dynmap", "updaterender", "Render updates starting at your location on all maps."), - new CommandInfo("dynmap", "updaterender", "", "Render updates starting at your location on map ."), - new CommandInfo("dynmap", "updaterender", " ", "Render updates starting at location , on world for map ."), - new CommandInfo("dynmap", "cancelrender", "Cancels any active renders on current world."), - new CommandInfo("dynmap", "cancelrender", "", "Cancels any active renders of world ."), - new CommandInfo("dynmap", "stats", "Show render statistics."), - new CommandInfo("dynmap", "triggerstats", "Show render update trigger statistics."), - new CommandInfo("dynmap", "resetstats", "Reset render statistics."), - new CommandInfo("dynmap", "sendtoweb", "", "Send message to web users."), - new CommandInfo("dynmap", "purgequeue", "Empty all pending tile updates from update queue."), - new CommandInfo("dynmap", "purgequeue", "", "Empty all pending tile updates from update queue for world ."), - new CommandInfo("dynmap", "purgemap", " ", "Delete all existing tiles for map on world ."), - new CommandInfo("dynmap", "purgeworld", "", "Delete all existing directories for world ."), - new CommandInfo("dynmap", "pause", "Show render pause state."), - new CommandInfo("dynmap", "pause", "", "Set render pause state."), - new CommandInfo("dynmap", "quiet", "Stop output from active jobs."), - new CommandInfo("dynmap", "ids-for-ip", "", "Show player IDs that have logged in from address ."), - new CommandInfo("dynmap", "ips-for-id", "", "Show IP addresses that have been used for player ."), - new CommandInfo("dynmap", "add-id-for-ip", " ", "Associate player with IP address ."), - new CommandInfo("dynmap", "del-id-for-ip", " ", "Disassociate player from IP address ."), - new CommandInfo("dynmap", "webregister", "Start registration process for creating web login account"), - new CommandInfo("dynmap", "webregister", "", "Start registration process for creating web login account for player "), - new CommandInfo("dynmap", "version", "Return version information"), - new CommandInfo("dynmap", "dumpmemory", "Return mempry use information"), - new CommandInfo("dynmap", "url", "Return confgured URL for Dynmap web"), - new CommandInfo("dmarker", "", "Manipulate map markers."), - new CommandInfo("dmarker", "add", "