├── .travis.yml ├── gw_datapack ├── pack.mcmeta └── data │ └── gw │ └── advancements │ ├── co2_can_play_at_that_game.json │ ├── home_grown.json │ ├── carbon_diet.json │ ├── palm_oil.json │ ├── smoke_em_if_you_got_em.json │ ├── root.json │ ├── green_thumbs_up.json │ ├── forest_bump.json │ └── if_a_tree_falls.json ├── src ├── main │ ├── resources │ │ ├── scripts │ │ │ ├── database.sql │ │ │ ├── players.sql │ │ │ ├── worlds.sql │ │ │ ├── entities.sql │ │ │ ├── furnaces.sql │ │ │ ├── reductions.sql │ │ │ ├── contributions.sql │ │ │ ├── trees.sql │ │ │ └── offsets.sql │ │ ├── models │ │ │ ├── entityMethaneModel.json │ │ │ ├── carbonIndexModel.json │ │ │ ├── reductionModel.json │ │ │ ├── scoreTempModel.json │ │ │ └── fuelModel.json │ │ ├── world.yml │ │ ├── config.yml │ │ └── plugin.yml │ └── java │ │ └── net │ │ └── porillo │ │ ├── database │ │ ├── api │ │ │ ├── SelectCallback.java │ │ │ ├── InsertQuery.java │ │ │ ├── DeleteQuery.java │ │ │ ├── Query.java │ │ │ ├── UpdateQuery.java │ │ │ └── SelectQuery.java │ │ ├── tables │ │ │ ├── ReductionTable.java │ │ │ ├── ContributionTable.java │ │ │ ├── TreeTable.java │ │ │ ├── FurnaceTable.java │ │ │ ├── TrackedBlockTable.java │ │ │ ├── Table.java │ │ │ ├── WorldTable.java │ │ │ ├── EntityTable.java │ │ │ └── PlayerTable.java │ │ ├── queries │ │ │ ├── other │ │ │ │ └── CreateTableQuery.java │ │ │ ├── delete │ │ │ │ ├── TreeDeleteQuery.java │ │ │ │ ├── FurnaceDeleteQuery.java │ │ │ │ └── EntityDeleteQuery.java │ │ │ ├── update │ │ │ │ ├── FurnaceUpdateQuery.java │ │ │ │ ├── TreeUpdateQuery.java │ │ │ │ ├── EntityUpdateQuery.java │ │ │ │ ├── PlayerUpdateQuery.java │ │ │ │ ├── WorldUpdateQuery.java │ │ │ │ └── OffsetUpdateQuery.java │ │ │ ├── select │ │ │ │ ├── WorldSelectQuery.java │ │ │ │ ├── TreeSelectQuery.java │ │ │ │ ├── PlayerSelectQuery.java │ │ │ │ ├── FurnaceSelectQuery.java │ │ │ │ ├── OffsetSelectQuery.java │ │ │ │ └── EntitySelectQuery.java │ │ │ └── insert │ │ │ │ ├── PlayerInsertQuery.java │ │ │ │ ├── ReductionInsertQuery.java │ │ │ │ ├── WorldInsertQuery.java │ │ │ │ ├── EntityInsertQuery.java │ │ │ │ ├── ContributionInsertQuery.java │ │ │ │ ├── FurnaceInsertQuery.java │ │ │ │ ├── TreeInsertQuery.java │ │ │ │ └── OffsetInsertQuery.java │ │ ├── queue │ │ │ └── ConcurrentHashQueue.java │ │ ├── TableManager.java │ │ └── ConnectionManager.java │ │ ├── objects │ │ ├── TrackedBlock.java │ │ ├── GChunk.java │ │ ├── TrackedEntity.java │ │ ├── Reduction.java │ │ ├── Contribution.java │ │ ├── Furnace.java │ │ ├── Tree.java │ │ ├── GWorld.java │ │ └── GPlayer.java │ │ ├── engine │ │ ├── api │ │ │ ├── FireDistribution.java │ │ │ ├── MobDistribution.java │ │ │ ├── Distribution.java │ │ │ └── Model.java │ │ ├── models │ │ │ ├── ReductionModel.java │ │ │ ├── CarbonIndexModel.java │ │ │ ├── EntityFitnessModel.java │ │ │ ├── FuelModel.java │ │ │ ├── EntityMethaneModel.java │ │ │ └── ScoreTempModel.java │ │ └── ClimateEngine.java │ │ ├── effect │ │ ├── ClimateData.java │ │ ├── api │ │ │ ├── ClimateEffectType.java │ │ │ ├── ListenerClimateEffect.java │ │ │ ├── ScheduleClimateEffect.java │ │ │ └── ClimateEffect.java │ │ ├── negative │ │ │ ├── formation │ │ │ │ ├── IceForm.java │ │ │ │ └── SnowForm.java │ │ │ ├── PermanentSlowness.java │ │ │ └── Fire.java │ │ ├── neutral │ │ │ ├── FarmYield.java │ │ │ ├── MobSpawningRate.java │ │ │ └── Weather.java │ │ ├── EffectModel.java │ │ ├── storage │ │ │ └── EffectData.java │ │ └── EffectEngine.java │ │ ├── listeners │ │ ├── WorldListener.java │ │ └── PlayerListener.java │ │ ├── util │ │ ├── AlertManager.java │ │ ├── ChunkSorter.java │ │ └── Colorizer.java │ │ ├── papi │ │ └── TemperatureExpansion.java │ │ └── config │ │ ├── GlobalWarmingConfig.java │ │ ├── ConfigLoader.java │ │ ├── WorldConfig.java │ │ └── Lang.java └── test │ ├── resources │ ├── scripts │ │ ├── database.sql │ │ ├── reductions.sql │ │ ├── contributions.sql │ │ ├── players.sql │ │ ├── worlds.sql │ │ ├── furnaces.sql │ │ ├── trees.sql │ │ └── offsets.sql │ ├── models │ │ └── world │ │ │ ├── entityMethaneModel.json │ │ │ ├── carbonIndexModel.json │ │ │ ├── reductionModel.json │ │ │ ├── scoreTempModel.json │ │ │ └── fuelModel.json │ └── testng.xml │ └── java │ └── net │ └── porillo │ ├── database │ ├── TableSelectTest.java │ ├── TestUtility.java │ ├── TableCreationTest.java │ ├── TableUpdateTest.java │ └── TableInsertTest.java │ ├── FuelModelTest.java │ ├── engine │ └── api │ │ └── DistributionTest.java │ └── ModelTest.java ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore └── launch.sh /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: openjdk8 3 | sudo: false 4 | notifications: 5 | email: false -------------------------------------------------------------------------------- /gw_datapack/pack.mcmeta: -------------------------------------------------------------------------------- 1 | {"pack":{"pack_format":1,"description":"Global warming-related advancements"}} -------------------------------------------------------------------------------- /src/main/resources/scripts/database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS GlobalWarming; 2 | USE GlobalWarming; -------------------------------------------------------------------------------- /src/test/resources/scripts/database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS GlobalWarming; 2 | USE GlobalWarming; -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/SelectCallback.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import java.util.List; 4 | 5 | public interface SelectCallback { 6 | 7 | void onSelectionCompletion(List returnList); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/models/entityMethaneModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "CHICKEN": 2.0, 3 | "COW": 4.0, 4 | "DONKEY": 3.0, 5 | "FOX": 2.0, 6 | "HORSE": 5.0, 7 | "LLAMA": 4.5, 8 | "MUSHROOM_COW": 4.0, 9 | "PIG": 3.5, 10 | "WOLF": 2.5 11 | } -------------------------------------------------------------------------------- /src/test/resources/scripts/reductions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS reductions ( 2 | uniqueId INT PRIMARY KEY, 3 | reductionerId INT NOT NULL, 4 | reductionKey INT NOT NULL, 5 | worldId VARCHAR(36) NOT NULL, 6 | value SMALLINT NOT NULL 7 | ); -------------------------------------------------------------------------------- /src/test/resources/models/world/entityMethaneModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "CHICKEN": 2.0, 3 | "COW": 4.0, 4 | "DONKEY": 3.0, 5 | "FOX": 2.0, 6 | "HORSE": 5.0, 7 | "LLAMA": 4.5, 8 | "MUSHROOM_COW": 4.0, 9 | "PIG": 3.5, 10 | "WOLF": 2.5 11 | } -------------------------------------------------------------------------------- /src/main/resources/scripts/players.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS players ( 2 | uniqueId INT PRIMARY KEY, 3 | uuid VARCHAR(36) UNIQUE NOT NULL, 4 | firstSeen BIGINT NOT NULL, 5 | carbonScore MEDIUMINT NOT NULL, 6 | worldId VARCHAR(36) NOT NULL 7 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/contributions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS contributions ( 2 | uniqueId INT PRIMARY KEY, 3 | contributerId INT NOT NULL, 4 | contributionKey INT NOT NULL, 5 | worldId VARCHAR(36) NOT NULL, 6 | value SMALLINT NOT NULL 7 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/players.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS players ( 2 | uniqueId INT PRIMARY KEY, 3 | uuid VARCHAR(36) UNIQUE NOT NULL, 4 | firstSeen BIGINT NOT NULL, 5 | carbonScore MEDIUMINT NOT NULL, 6 | worldId VARCHAR(36) NOT NULL 7 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/worlds.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS worlds ( 2 | uniqueId INT PRIMARY KEY, 3 | worldId VARCHAR(36) UNIQUE NOT NULL, 4 | firstSeen BIGINT NOT NULL, 5 | carbonValue MEDIUMINT NOT NULL, 6 | seaLevel TINYINT NOT NULL, 7 | size SMALLINT 8 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/worlds.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS worlds ( 2 | uniqueID INT PRIMARY KEY, 3 | worldId VARCHAR(36) UNIQUE NOT NULL, 4 | firstSeen BIGINT NOT NULL, 5 | carbonValue MEDIUMINT NOT NULL, 6 | seaLevel TINYINT NOT NULL, 7 | size SMALLINT 8 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/entities.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS entities ( 2 | uniqueId INT PRIMARY KEY, 3 | uuid VARCHAR(36) UNIQUE NOT NULL, 4 | breederId INT NOT NULL, 5 | entityType VARCHAR(64) NOT NULL, 6 | ticksLived INT NOT NULL, 7 | alive BOOL NOT NULL 8 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/furnaces.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS furnaces ( 2 | uniqueId INT PRIMARY KEY, 3 | ownerId INT NOT NULL, 4 | worldId VARCHAR(36) NOT NULL, 5 | blockX INT NOT NULL, 6 | blockY INT NOT NULL, 7 | blockZ INT NOT NULL, 8 | active BOOL NOT NULL 9 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/furnaces.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS furnaces ( 2 | uniqueID INT PRIMARY KEY, 3 | ownerID INT NOT NULL, 4 | worldId VARCHAR(36) NOT NULL, 5 | blockX INT NOT NULL, 6 | blockY INT NOT NULL, 7 | blockZ INT NOT NULL, 8 | active BOOL NOT NULL 9 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/reductions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS reductions ( 2 | uniqueId INT PRIMARY KEY, 3 | reductionerId INT NOT NULL, 4 | reductionKey INT NOT NULL, 5 | worldId VARCHAR(36) NOT NULL, 6 | time TIMESTAMP NOT NULL DEFAULT now(), 7 | value SMALLINT NOT NULL 8 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/contributions.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS contributions ( 2 | uniqueId INT PRIMARY KEY, 3 | contributerId INT NOT NULL, 4 | contributionKey INT NOT NULL, 5 | worldId VARCHAR(36) NOT NULL, 6 | time TIMESTAMP NOT NULL DEFAULT now(), 7 | value SMALLINT NOT NULL 8 | ); -------------------------------------------------------------------------------- /src/main/resources/scripts/trees.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS trees ( 2 | uniqueId INT PRIMARY KEY, 3 | ownerId INT NOT NULL, 4 | worldId VARCHAR(36) NOT NULL, 5 | blockX INT NOT NULL, 6 | blockY INT NOT NULL, 7 | blockZ INT NOT NULL, 8 | sapling BOOL NOT NULL, 9 | size SMALLINT 10 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/trees.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS trees ( 2 | uniqueId INT PRIMARY KEY, 3 | ownerId INT NOT NULL, 4 | worldId VARCHAR(36) NOT NULL, 5 | blockX INT NOT NULL, 6 | blockY INT NOT NULL, 7 | blockZ INT NOT NULL, 8 | sapling BOOL NOT NULL, 9 | size SMALLINT 10 | ); -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/InsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | public abstract class InsertQuery implements Query { 8 | 9 | @Getter 10 | private String table; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/DeleteQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | public abstract class DeleteQuery implements Query { 8 | 9 | @Getter 10 | private String table; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/scripts/offsets.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS offsets ( 2 | uniqueId INT PRIMARY KEY, 3 | creatorId INT NOT NULL, 4 | hunterId INT, 5 | worldId VARCHAR(36) NOT NULL, 6 | logBlocksTarget SMALLINT NOT NULL, 7 | reward INT NOT NULL, 8 | timeStarted BIGINT NOT NULL, 9 | timeCompleted BIGINT 10 | ); -------------------------------------------------------------------------------- /src/test/resources/scripts/offsets.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS offsets ( 2 | uniqueID INT PRIMARY KEY, 3 | creatorId INT NOT NULL, 4 | hunterId INT, 5 | worldId VARCHAR(36) NOT NULL, 6 | logBlocksTarget SMALLINT NOT NULL, 7 | reward INT NOT NULL, 8 | timeStarted BIGINT NOT NULL, 9 | timeCompleted BIGINT 10 | ); -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/TrackedBlock.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.*; 4 | import org.bukkit.Location; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | @AllArgsConstructor 9 | public class TrackedBlock { 10 | 11 | private Integer uniqueId; 12 | private Integer ownerId; 13 | private Location location; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/models/carbonIndexModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "-1000000": 10.0, 3 | "-500000": 9.0, 4 | "-250000": 8.0, 5 | "-100000": 7.0, 6 | "-25000": 6.0, 7 | "-1000": 5.5, 8 | "-100": 5.1, 9 | "0": 5.0, 10 | "100": 4.9, 11 | "1000": 4.5, 12 | "10000": 4.0, 13 | "50000": 3.0, 14 | "200000": 2.0, 15 | "500000": 1.0, 16 | "1000000": 0.0 17 | } -------------------------------------------------------------------------------- /src/test/resources/models/world/carbonIndexModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "-1000000": 10.0, 3 | "-500000": 9.0, 4 | "-250000": 8.0, 5 | "-100000": 7.0, 6 | "-25000": 6.0, 7 | "-1000": 5.5, 8 | "-100": 5.1, 9 | "0": 5.0, 10 | "100": 4.9, 11 | "1000": 4.5, 12 | "10000": 4.0, 13 | "50000": 3.0, 14 | "200000": 2.0, 15 | "500000": 1.0, 16 | "1000000": 0.0 17 | } -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/api/FireDistribution.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.api; 2 | 3 | public class FireDistribution extends Distribution { 4 | 5 | public FireDistribution(double[] temp, double[] fitness, double[] blocks) { 6 | super(temp, blocks); 7 | } 8 | 9 | public double getBlocks(double input) { 10 | return super.getValue(input); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/api/MobDistribution.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.api; 2 | 3 | import lombok.Getter; 4 | 5 | public class MobDistribution extends Distribution { 6 | 7 | @Getter private String alternate; 8 | 9 | public MobDistribution(double[] temp, double[] fitness, String alternate) { 10 | super(temp, fitness); 11 | this.alternate = alternate; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/Query.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.SQLException; 6 | import java.sql.Statement; 7 | 8 | public interface Query { 9 | 10 | String getTable(); 11 | 12 | String getSQL(); 13 | 14 | Statement prepareStatement(Connection connection) throws SQLException; 15 | } 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /src/main/resources/world.yml: -------------------------------------------------------------------------------- 1 | enabled: true 2 | association: world 3 | carbonSensitivity: LOW 4 | blastFurnaceMultiplier: 1.2 5 | methaneTicksLivedModifier: 0.001 6 | bonemealReductionAllowed: true 7 | bonemealReductionModifier: 0.5 8 | enabledEffects: 9 | - SEA_LEVEL_RISE 10 | - ICE_MELT 11 | - SNOW_MELT 12 | - ICE_FORMATION 13 | - SNOW_FORMATION 14 | - AREA_POTION_CLOUD 15 | - FARM_YIELD 16 | - MOB_SPAWN_RATE 17 | - WEATHER 18 | - PERMANENT_SLOWNESS 19 | - FIRE 20 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/ReductionTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.Getter; 4 | import net.porillo.objects.Reduction; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ReductionTable extends Table { 10 | 11 | @Getter private List reductions = new ArrayList<>(); 12 | 13 | public ReductionTable() { 14 | super("reductions"); 15 | createIfNotExists(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/ContributionTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.Getter; 4 | import net.porillo.objects.Contribution; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class ContributionTable extends Table { 10 | 11 | @Getter private final List contributions = new ArrayList<>(); 12 | 13 | public ContributionTable() { 14 | super("contributions"); 15 | createIfNotExists(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/ClimateData.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect; 2 | 3 | import net.porillo.effect.api.ClimateEffectType; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target(ElementType.TYPE) 12 | public @interface ClimateData { 13 | 14 | ClimateEffectType type(); 15 | 16 | boolean provideModel() default true; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | bounty: 2 | max-created-per-player: 5 3 | chat: 4 | welcome-on-join: true 5 | table-width: 280 6 | climate-notification: 7 | degrees-until-change-detected: 0.25 8 | commands: 9 | spam-interval: 60 10 | database: 11 | type: H2 12 | host: localhost 13 | port: 3306 14 | name: GlobalWarming 15 | username: user 16 | password: pass 17 | interval: 300 18 | notification: 19 | interval: 6000 20 | duration: 300 21 | scoreboard: 22 | enabled: true 23 | interval: 20 24 | temperature: 25 | format: '#.##' 26 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/api/ClimateEffectType.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.api; 2 | 3 | public enum ClimateEffectType { 4 | 5 | // Returned by ClimateEffect in case @ClimateData is nonexistent 6 | NONE, 7 | 8 | // Climate Damages 9 | SEA_LEVEL_RISE, 10 | 11 | ICE_MELT, 12 | SNOW_MELT, 13 | ICE_FORMATION, 14 | SNOW_FORMATION, 15 | 16 | AREA_POTION_CLOUD, 17 | 18 | FARM_YIELD, 19 | MOB_SPAWN_RATE, 20 | WEATHER, 21 | 22 | PERMANENT_SLOWNESS, 23 | FIRE 24 | 25 | // TODO: Add more 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/models/reductionModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "DIRT": 0.0, 3 | "ACACIA_LEAVES": 0.2, 4 | "ACACIA_LOG": 2.0, 5 | "ACACIA_SAPLING": 1.0, 6 | "BIRCH_LEAVES": 0.2, 7 | "BIRCH_LOG": 2.0, 8 | "BIRCH_SAPLING": 1.0, 9 | "DARK_OAK_LEAVES": 0.2, 10 | "DARK_OAK_LOG": 1.0, 11 | "DARK_SAPLING": 1.0, 12 | "JUNGLE_LEAVES": 0.2, 13 | "JUNGLE_LOG": 2.0, 14 | "JUNGLE_SAPLING": 1.0, 15 | "SPRUCE_LOG": 2.0, 16 | "SPRUCE_LEAVES": 0.2, 17 | "SPRUCE_SAPLING": 1.0, 18 | "OAK_LEAVES": 0.2, 19 | "OAK_LOG": 2.0, 20 | "OAK_SAPLING": 1.0, 21 | "VINE": 0.3 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/database/TableSelectTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import net.porillo.database.queue.AsyncDBQueue; 4 | import org.testng.annotations.Test; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | 9 | public class TableSelectTest { 10 | 11 | @Test 12 | public void testTableSelections() throws SQLException, ClassNotFoundException { 13 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 14 | AsyncDBQueue.getInstance().writeSelectQueue(connection); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/resources/models/world/reductionModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "DIRT": 0.0, 3 | "ACACIA_LEAVES": 0.2, 4 | "ACACIA_LOG": 2.0, 5 | "ACACIA_SAPLING": 1.0, 6 | "BIRCH_LEAVES": 0.2, 7 | "BIRCH_LOG": 2.0, 8 | "BIRCH_SAPLING": 1.0, 9 | "DARK_OAK_LEAVES": 0.2, 10 | "DARK_OAK_LOG": 1.0, 11 | "DARK_SAPLING": 1.0, 12 | "JUNGLE_LEAVES": 0.2, 13 | "JUNGLE_LOG": 2.0, 14 | "JUNGLE_SAPLING": 1.0, 15 | "SPRUCE_LOG": 2.0, 16 | "SPRUCE_LEAVES": 0.2, 17 | "SPRUCE_SAPLING": 1.0, 18 | "OAK_LEAVES": 0.2, 19 | "OAK_LOG": 2.0, 20 | "OAK_SAPLING": 1.0, 21 | "VINE": 0.3, 22 | "BEEHIVE": 0.0 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/api/ListenerClimateEffect.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.api; 2 | 3 | import net.porillo.GlobalWarming; 4 | import org.bukkit.event.Listener; 5 | 6 | public abstract class ListenerClimateEffect extends ClimateEffect implements Listener { 7 | 8 | @Override 9 | public void onPluginEnable() { 10 | GlobalWarming.getInstance().getLogger().info("Loading Climate Effect " + super.getName()); 11 | } 12 | 13 | @Override 14 | public void onPluginDisable() { 15 | GlobalWarming.getInstance().getLogger().info("Unloading Climate Effect " + super.getName()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/GChunk.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.Data; 4 | import org.bukkit.Chunk; 5 | import org.bukkit.ChunkSnapshot; 6 | 7 | @Data 8 | public class GChunk { 9 | 10 | private String world; 11 | private int x, z; 12 | 13 | public GChunk(ChunkSnapshot chunkSnapshot) { 14 | this.world = chunkSnapshot.getWorldName(); 15 | this.x = chunkSnapshot.getX(); 16 | this.z = chunkSnapshot.getZ(); 17 | } 18 | 19 | public GChunk(Chunk chunk) { 20 | this.world = chunk.getWorld().getName(); 21 | this.x = chunk.getX(); 22 | this.z = chunk.getZ(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/other/CreateTableQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.other; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import net.porillo.database.api.Query; 6 | 7 | import java.sql.Connection; 8 | import java.sql.PreparedStatement; 9 | import java.sql.SQLException; 10 | 11 | @AllArgsConstructor 12 | public class CreateTableQuery implements Query { 13 | 14 | @Getter 15 | private String table; 16 | @Getter 17 | private String SQL; 18 | 19 | @Override 20 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 21 | return connection.prepareStatement(this.SQL); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/api/ScheduleClimateEffect.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.api; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.porillo.GlobalWarming; 6 | 7 | public abstract class ScheduleClimateEffect extends ClimateEffect implements Runnable { 8 | 9 | @Getter @Setter private int period; 10 | @Getter @Setter private int taskId; 11 | 12 | @Override 13 | public void onPluginEnable() { 14 | GlobalWarming.getInstance().getLogger().info("Loading Climate Effect " + super.getName()); 15 | } 16 | 17 | @Override 18 | public void onPluginDisable() { 19 | GlobalWarming.getInstance().getLogger().info("Unloading Climate Effect " + super.getName()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/listeners/WorldListener.java: -------------------------------------------------------------------------------- 1 | package net.porillo.listeners; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.engine.ClimateEngine; 6 | import org.bukkit.event.EventHandler; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.world.WorldLoadEvent; 9 | 10 | @RequiredArgsConstructor 11 | public class WorldListener implements Listener { 12 | 13 | private final GlobalWarming gw; 14 | 15 | @EventHandler(ignoreCancelled = true) 16 | public void onWorldLoad(WorldLoadEvent loadEvent) { 17 | gw.getLogger().info("Detected world load after GW enabled, triggering automatic climate engine load."); 18 | ClimateEngine.getInstance().loadWorldClimateEngine(loadEvent.getWorld()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/co2_can_play_at_that_game.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "CO2 Can Play at That Game" 5 | }, 6 | "description": { 7 | "text": "Place a furnace. Trivia: carbon dioxide enters the atmosphere when fossil fuels (coal, natural gas and oil), solid waste, trees and wood products burn [EPA, 2016]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:furnace" 11 | }, 12 | "frame": "task", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "rewards": { 18 | "experience": 2 19 | }, 20 | "criteria": { 21 | "furnace_placed": { 22 | "trigger": "minecraft:placed_block", 23 | "conditions": { 24 | "block": "minecraft:furnace" 25 | } 26 | } 27 | }, 28 | "parent": "gw:root" 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/UpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor 7 | public abstract class UpdateQuery implements Query { 8 | 9 | @Getter 10 | private String table; 11 | @Getter 12 | private T object; 13 | 14 | @Override 15 | public boolean equals(Object o) { 16 | if (this == o) return true; 17 | if (o == null || getClass() != o.getClass()) return false; 18 | 19 | UpdateQuery that = (UpdateQuery) o; 20 | 21 | return table.equals(that.table) && object.equals(that.object); 22 | } 23 | 24 | @Override 25 | public int hashCode() { 26 | int result = table.hashCode(); 27 | result = 31 * result + object.hashCode(); 28 | return result; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/FuelModelTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.porillo.engine.models.FuelModel; 6 | import org.bukkit.Material; 7 | import org.testng.Assert; 8 | import org.testng.annotations.Test; 9 | 10 | import java.util.Map; 11 | 12 | public class FuelModelTest { 13 | 14 | @Test 15 | public void fuelModelContainsAllMaterials() { 16 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 17 | FuelModel model = new FuelModel(gson, "world"); 18 | Map existingFuelMap = model.getFuelMap(); 19 | for (Material material : Material.values()) { 20 | if (material.isFuel()) { 21 | Assert.assertTrue(existingFuelMap.containsKey(material), material.name()); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/api/SelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.api; 2 | 3 | import lombok.Getter; 4 | import lombok.ToString; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | import java.util.List; 10 | 11 | @Getter 12 | @ToString 13 | public abstract class SelectQuery> implements Query { 14 | 15 | private String table; 16 | private Callback callback; 17 | 18 | public SelectQuery(String table, Callback callback) { 19 | this.table = table; 20 | this.callback = callback; 21 | } 22 | 23 | public abstract List queryDatabase(Connection connection) throws SQLException; 24 | 25 | public void execute(Connection connection) throws SQLException { 26 | this.callback.onSelectionCompletion(queryDatabase(connection)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/home_grown.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "Home Grown" 5 | }, 6 | "description": { 7 | "text": "Plant a dozen carrots. Trivia: food-related emissions are mostly in the production phase, not transportation [Weber et. al, 2008]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:carrot" 11 | }, 12 | "frame": "task", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "rewards": { 18 | "experience": 2 19 | }, 20 | "criteria": { 21 | "home_grown_carrot": { 22 | "trigger": "minecraft:placed_block", 23 | "conditions": { 24 | "block": "minecraft:carrots", 25 | "item": { 26 | "count": { 27 | "min": 12, 28 | "max": 12 29 | } 30 | } 31 | } 32 | } 33 | }, 34 | "parent": "gw:root" 35 | } 36 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/carbon_diet.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "The Carbon Diet" 5 | }, 6 | "description": { 7 | "text": "Eat a half-dozen carrots. Trivia: a reduction in meat consumption would lower dietary-related emissions [Scarborough, et. al, 2014]" 8 | }, 9 | "icon": { 10 | "item": "minecraft:grass" 11 | }, 12 | "frame": "challenge", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "rewards": { 18 | "experience": 5 19 | }, 20 | "criteria": { 21 | "carbon_diet_carrots": { 22 | "trigger": "minecraft:consume_item", 23 | "conditions": { 24 | "item": { 25 | "item": "minecraft:carrot", 26 | "count": { 27 | "min": 6, 28 | "max": 6 29 | } 30 | } 31 | } 32 | } 33 | }, 34 | "parent": "gw:home_grown" 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/TrackedEntity.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.*; 4 | import org.bukkit.entity.EntityType; 5 | 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.util.UUID; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class TrackedEntity { 14 | 15 | private Integer uniqueId; 16 | private UUID uuid; 17 | private Integer breederId; 18 | private EntityType entityType; 19 | private long ticksLived; 20 | private boolean alive; 21 | 22 | public TrackedEntity(ResultSet rs) throws SQLException { 23 | super(); 24 | setUniqueId(rs.getInt(1)); 25 | setUuid(UUID.fromString(rs.getString(2))); 26 | setBreederId(rs.getInt(3)); 27 | setEntityType(EntityType.valueOf(rs.getString(4).toUpperCase())); 28 | setTicksLived(rs.getLong(5)); 29 | setAlive(rs.getBoolean(6)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # BlueJ files 4 | *.ctxt 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | *.jar 11 | *.war 12 | *.ear 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # Windows 22 | # ========================= 23 | 24 | # Windows thumbnail cache files 25 | Thumbs.db 26 | ehthumbs.db 27 | ehthumbs_vista.db 28 | 29 | # Folder config file 30 | Desktop.ini 31 | 32 | # Recycle Bin used on file shares 33 | $RECYCLE.BIN/ 34 | 35 | # Windows Installer files 36 | *.cab 37 | *.msi 38 | *.msm 39 | *.msp 40 | 41 | # Windows shortcuts 42 | *.lnk 43 | 44 | # intellij 45 | *.iml 46 | *.ipr 47 | *.iws 48 | .idea/ 49 | 50 | out/ 51 | .idea/**/libraries 52 | /target/ 53 | 54 | .classpath 55 | .project 56 | .settings/ 57 | 58 | dependency-reduced-pom.xml 59 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: GlobalWarming 2 | author: milkywayz 3 | main: net.porillo.GlobalWarming 4 | version: ${project.version} 5 | api-version: 1.13 6 | softdepend: [Vault, PlaceholderAPI] 7 | commands: 8 | globalwarming: 9 | aliases: gw 10 | description: The main command for GlobalWarming 11 | permissions: 12 | globalwarming.booklet: 13 | default: true 14 | globalwarming.bounty: 15 | default: true 16 | globalwarming.bounty.create: 17 | default: true 18 | globalwarming.bounty.join: 19 | default: true 20 | globalwarming.bounty.cancel: 21 | default: true 22 | globalwarming.help: 23 | default: true 24 | globalwarming.score: 25 | default: true 26 | globalwarming.score.show: 27 | default: true 28 | globalwarming.score.hide: 29 | default: true 30 | globalwarming.top: 31 | default: true 32 | globalwarming.top.polluter: 33 | default: true 34 | globalwarming.top.planter: 35 | default: true 36 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/engine/api/DistributionTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.api; 2 | 3 | import org.testng.Assert; 4 | import org.testng.annotations.Test; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | @Test 10 | public class DistributionTest { 11 | 12 | private final static List x = Arrays.asList(0D, 1D, 2D, 3D, 4D); 13 | private final static List y = Arrays.asList(0D, 10D, 25D, 50D, 100D); 14 | 15 | @Test 16 | public void testDistributionMin() { 17 | Distribution distribution = new Distribution(x, y); 18 | Assert.assertEquals(distribution.getValue(0), 0D); 19 | Assert.assertEquals(distribution.getValue(1), 10D); 20 | Assert.assertEquals(distribution.getValue(2), 25D); 21 | Assert.assertEquals(distribution.getValue(3), 50D); 22 | System.out.println(distribution.getValue(3.95)); 23 | Assert.assertEquals(distribution.getValue(4), 100D); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/delete/TreeDeleteQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.delete; 2 | 3 | import net.porillo.database.api.DeleteQuery; 4 | import net.porillo.objects.Tree; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class TreeDeleteQuery extends DeleteQuery { 11 | 12 | private Tree tree; 13 | 14 | public TreeDeleteQuery(Tree tree) { 15 | super("trees"); 16 | this.tree = tree; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "DELETE FROM trees WHERE uniqueId = ?"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, tree.getUniqueId()); 28 | return preparedStatement; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/delete/FurnaceDeleteQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.delete; 2 | 3 | import net.porillo.database.api.DeleteQuery; 4 | import net.porillo.objects.Furnace; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class FurnaceDeleteQuery extends DeleteQuery { 11 | 12 | private Furnace furnace; 13 | 14 | public FurnaceDeleteQuery(Furnace furnace) { 15 | super("furnaces"); 16 | this.furnace = furnace; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "DELETE FROM furnaces WHERE uniqueId = ?"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, furnace.getUniqueId()); 28 | return preparedStatement; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/delete/EntityDeleteQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.delete; 2 | 3 | import net.porillo.database.api.DeleteQuery; 4 | import net.porillo.objects.TrackedEntity; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class EntityDeleteQuery extends DeleteQuery { 11 | 12 | private TrackedEntity entity; 13 | 14 | public EntityDeleteQuery(TrackedEntity entity) { 15 | super("entities"); 16 | this.entity = entity; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "DELETE FROM entities WHERE uniqueId = ?"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, entity.getUniqueId()); 28 | return preparedStatement; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/palm_oil.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "In the Palm of Your Hand" 5 | }, 6 | "description": { 7 | "text": "Cut down a jungle tree. Trivia: when tropical forests are cleared to make way for oil palm plantations, carbon is released into the atmosphere as carbon dioxide [UCS, 2013]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:jungle_log" 11 | }, 12 | "frame": "task", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "rewards": { 18 | "experience": 2 19 | }, 20 | "criteria": { 21 | "palm_oil_log": { 22 | "trigger": "minecraft:inventory_changed", 23 | "conditions": { 24 | "items": [ 25 | { 26 | "item": "minecraft:jungle_log", 27 | "count": { 28 | "min": 1, 29 | "max": 1 30 | } 31 | } 32 | ] 33 | } 34 | } 35 | }, 36 | "parent": "gw:if_a_tree_falls" 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/FurnaceUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import net.porillo.database.api.UpdateQuery; 4 | import net.porillo.objects.Furnace; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class FurnaceUpdateQuery extends UpdateQuery { 11 | 12 | public FurnaceUpdateQuery(Furnace furnace) { 13 | super("furnaces", furnace); 14 | } 15 | 16 | @Override 17 | public String getSQL() { 18 | return "UPDATE furnaces SET active = ? WHERE uniqueId = ?"; 19 | } 20 | 21 | @Override 22 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 23 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 24 | preparedStatement.setBoolean(1, getObject().isActive()); 25 | preparedStatement.setInt(2, getObject().getUniqueId()); 26 | return preparedStatement; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/smoke_em_if_you_got_em.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "Smoke 'Em if You Got 'Em" 5 | }, 6 | "description": { 7 | "text": "Smelt 64 iron ingots. Trivia: emissions from industry are primarily from burning fossil fuels and in chemical reactions for producing goods [EPA, 2016]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:iron_ingot" 11 | }, 12 | "frame": "challenge", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "rewards": { 18 | "experience": 10 19 | }, 20 | "criteria": { 21 | "smoke_em_iron": { 22 | "trigger": "minecraft:inventory_changed", 23 | "conditions": { 24 | "items": [ 25 | { 26 | "item": "minecraft:iron_ingot", 27 | "count": { 28 | "min": 64, 29 | "max": 64 30 | } 31 | } 32 | ] 33 | } 34 | } 35 | }, 36 | "parent": "gw:co2_can_play_at_that_game" 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/TreeUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import net.porillo.database.api.UpdateQuery; 4 | import net.porillo.objects.Tree; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class TreeUpdateQuery extends UpdateQuery { 11 | 12 | public TreeUpdateQuery(Tree tree) { 13 | super("trees", tree); 14 | } 15 | 16 | @Override 17 | public String getSQL() { 18 | return "UPDATE trees SET sapling = ?, size = ? WHERE uniqueId = ?"; 19 | } 20 | 21 | @Override 22 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 23 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 24 | preparedStatement.setBoolean(1, getObject().isSapling()); 25 | preparedStatement.setInt(2, getObject().getSize()); 26 | preparedStatement.setInt(3, getObject().getUniqueId()); 27 | return preparedStatement; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/util/AlertManager.java: -------------------------------------------------------------------------------- 1 | package net.porillo.util; 2 | 3 | import net.porillo.objects.GPlayer; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | import java.util.UUID; 8 | 9 | public class AlertManager { 10 | 11 | private static AlertManager instance; // single instance 12 | 13 | private Set subscribers = new HashSet<>(); 14 | 15 | public void subscribe(UUID uuid) { 16 | this.subscribers.add(uuid); 17 | } 18 | 19 | public void unsubscribe(UUID uuid) { 20 | this.subscribers.remove(uuid); 21 | } 22 | 23 | public boolean isSubscribed(UUID uuid) { 24 | return subscribers.contains(uuid); 25 | } 26 | 27 | public void alert(GPlayer player, String message) { 28 | if (player == null) return; 29 | if (subscribers.contains(player.getUuid())) { 30 | player.sendMsg(message); 31 | } 32 | } 33 | 34 | public static AlertManager getInstance() { 35 | if (instance == null) instance = new AlertManager(); 36 | return instance; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/EntityUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import net.porillo.database.api.UpdateQuery; 4 | import net.porillo.objects.TrackedEntity; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class EntityUpdateQuery extends UpdateQuery { 11 | 12 | public EntityUpdateQuery(TrackedEntity entity) { 13 | super("entities", entity); 14 | } 15 | 16 | @Override 17 | public String getSQL() { 18 | return "UPDATE entities SET ticksLived = ?, alive = ? WHERE uniqueId = ?"; 19 | } 20 | 21 | @Override 22 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 23 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 24 | preparedStatement.setLong(1, getObject().getTicksLived()); 25 | preparedStatement.setBoolean(2, getObject().isAlive()); 26 | preparedStatement.setInt(3, getObject().getUniqueId()); 27 | return preparedStatement; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/PlayerUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import net.porillo.database.api.UpdateQuery; 4 | import net.porillo.objects.GPlayer; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class PlayerUpdateQuery extends UpdateQuery { 11 | 12 | public PlayerUpdateQuery(GPlayer gPlayer) { 13 | super("players", gPlayer); 14 | 15 | } 16 | 17 | @Override 18 | public String getSQL() { 19 | return "UPDATE players SET carbonScore = ?, worldId = ? WHERE uniqueId = ?"; 20 | } 21 | 22 | @Override 23 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 24 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 25 | preparedStatement.setInt(1, getObject().getCarbonScore()); 26 | preparedStatement.setString(2, getObject().getWorldId().toString()); 27 | preparedStatement.setInt(3, getObject().getUniqueId()); 28 | return preparedStatement; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/api/ClimateEffect.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.api; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.EffectEngine; 9 | 10 | @AllArgsConstructor 11 | public abstract class ClimateEffect { 12 | 13 | @Getter @Setter private JsonObject jsonModel; 14 | private ClimateData climateData; 15 | 16 | public ClimateEffect() { 17 | this.climateData = getClass().getAnnotation(ClimateData.class); 18 | } 19 | 20 | public String getName() { 21 | if (climateData != null) { 22 | return climateData.type().name(); 23 | } 24 | return ""; 25 | } 26 | 27 | public ClimateEffectType getType() { 28 | if (climateData != null) { 29 | return climateData.type(); 30 | } 31 | return ClimateEffectType.NONE; 32 | } 33 | 34 | public void unregister() { 35 | EffectEngine.getInstance().unregisterEffect(getType()); 36 | } 37 | 38 | public abstract void onPluginEnable(); 39 | 40 | public abstract void onPluginDisable(); 41 | } 42 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/root.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "Global Warming!" 5 | }, 6 | "description": { 7 | "text": "CO2 emissions and reductions will be tracked" 8 | }, 9 | "icon": { 10 | "item": "minecraft:oxeye_daisy" 11 | }, 12 | "frame": "task", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false, 16 | "background": "minecraft:textures/gui/advancements/backgrounds/stone.png" 17 | }, 18 | "requirements": [ 19 | [ 20 | "welcome_grass", 21 | "welcome_air", 22 | "welcome_water" 23 | ] 24 | ], 25 | "rewards": { 26 | "experience": 2 27 | }, 28 | "criteria": { 29 | "welcome_grass": { 30 | "trigger": "minecraft:enter_block", 31 | "conditions": { 32 | "block": "minecraft:grass_block" 33 | } 34 | }, 35 | "welcome_air": { 36 | "trigger": "minecraft:enter_block", 37 | "conditions": { 38 | "block": "minecraft:air" 39 | } 40 | }, 41 | "welcome_water": { 42 | "trigger": "minecraft:enter_block", 43 | "conditions": { 44 | "block": "minecraft:water" 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/WorldUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import lombok.ToString; 4 | import net.porillo.database.api.UpdateQuery; 5 | import net.porillo.objects.GWorld; 6 | 7 | import java.sql.Connection; 8 | import java.sql.PreparedStatement; 9 | import java.sql.SQLException; 10 | 11 | @ToString 12 | public class WorldUpdateQuery extends UpdateQuery { 13 | 14 | public WorldUpdateQuery(GWorld world) { 15 | super("worlds", world); 16 | } 17 | 18 | @Override 19 | public String getSQL() { 20 | return "UPDATE worlds SET carbonValue = ?, seaLevel = ?, size = ? WHERE uniqueId = ?"; 21 | } 22 | 23 | @Override 24 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 25 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 26 | preparedStatement.setInt(1, getObject().getCarbonValue()); 27 | preparedStatement.setInt(2, getObject().getSeaLevel()); 28 | preparedStatement.setInt(3, getObject().getSize()); 29 | preparedStatement.setInt(4, getObject().getUniqueID()); 30 | return preparedStatement; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/models/scoreTempModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "LOW": { 3 | "-1000000": 10.0, 4 | "-500000": 11.0, 5 | "-250000": 12.0, 6 | "-100000": 13.0, 7 | "-25000": 13.5, 8 | "-1000": 13.8, 9 | "-100": 13.9, 10 | "0": 14.0, 11 | "1": 14.0, 12 | "10": 14.0, 13 | "50": 14.1, 14 | "100": 14.2, 15 | "500": 14.3, 16 | "1000": 14.5, 17 | "5000": 14.75, 18 | "10000": 15.0, 19 | "50000": 15.5, 20 | "100000": 16.0, 21 | "250000": 17.0, 22 | "500000": 18.0, 23 | "750000": 19.0, 24 | "1000000": 20.0 25 | }, 26 | "HIGH": { 27 | "-50000": 10.0, 28 | "-25000": 11.0, 29 | "-12500": 12.0, 30 | "-5000": 13.0, 31 | "-1250": 13.5, 32 | "-50": 13.8, 33 | "-5": 13.9, 34 | "0": 14.0, 35 | "5": 14.2, 36 | "25": 14.3, 37 | "50": 14.5, 38 | "250": 14.75, 39 | "500": 15.0, 40 | "2500": 15.5, 41 | "5000": 16.0, 42 | "12500": 17.0, 43 | "25000": 18.0, 44 | "37500": 19.0, 45 | "50000": 20.0 46 | }, 47 | "VERY_HIGH": { 48 | "-800": 10.0, 49 | "-600": 11.0, 50 | "-400": 12.0, 51 | "-200": 13.0, 52 | "0": 14.0, 53 | "200": 15.0, 54 | "400": 16.0, 55 | "600": 17.0, 56 | "800": 18.0, 57 | "1000": 19.0, 58 | "1200": 20.0 59 | } 60 | } -------------------------------------------------------------------------------- /src/test/resources/models/world/scoreTempModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "LOW": { 3 | "-1000000": 10.0, 4 | "-500000": 11.0, 5 | "-250000": 12.0, 6 | "-100000": 13.0, 7 | "-25000": 13.5, 8 | "-1000": 13.8, 9 | "-100": 13.9, 10 | "0": 14.0, 11 | "1": 14.0, 12 | "10": 14.0, 13 | "50": 14.1, 14 | "100": 14.2, 15 | "500": 14.3, 16 | "1000": 14.5, 17 | "5000": 14.75, 18 | "10000": 15.0, 19 | "50000": 15.5, 20 | "100000": 16.0, 21 | "250000": 17.0, 22 | "500000": 18.0, 23 | "750000": 19.0, 24 | "1000000": 20.0 25 | }, 26 | "HIGH": { 27 | "-50000": 10.0, 28 | "-25000": 11.0, 29 | "-12500": 12.0, 30 | "-5000": 13.0, 31 | "-1250": 13.5, 32 | "-50": 13.8, 33 | "-5": 13.9, 34 | "0": 14.0, 35 | "5": 14.2, 36 | "25": 14.3, 37 | "50": 14.5, 38 | "250": 14.75, 39 | "500": 15.0, 40 | "2500": 15.5, 41 | "5000": 16.0, 42 | "12500": 17.0, 43 | "25000": 18.0, 44 | "37500": 19.0, 45 | "50000": 20.0 46 | }, 47 | "VERY_HIGH": { 48 | "-800": 10.0, 49 | "-600": 11.0, 50 | "-400": 12.0, 51 | "-200": 13.0, 52 | "0": 14.0, 53 | "200": 15.0, 54 | "400": 16.0, 55 | "600": 17.0, 56 | "800": 18.0, 57 | "1000": 19.0, 58 | "1200": 20.0 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/WorldSelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.WorldTable; 5 | import net.porillo.objects.GWorld; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class WorldSelectQuery extends SelectQuery { 15 | 16 | public WorldSelectQuery(WorldTable callback) { 17 | super("worlds", callback); 18 | } 19 | 20 | @Override 21 | public List queryDatabase(Connection connection) throws SQLException { 22 | List worlds = new ArrayList<>(); 23 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 24 | 25 | while (rs.next()) { 26 | worlds.add(new GWorld(rs)); 27 | } 28 | 29 | return worlds; 30 | } 31 | 32 | @Override 33 | public Statement prepareStatement(Connection connection) throws SQLException { 34 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 35 | } 36 | 37 | @Override 38 | public String getSQL() { 39 | return "SELECT * FROM worlds"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/TreeSelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.TreeTable; 5 | import net.porillo.objects.Tree; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class TreeSelectQuery extends SelectQuery { 15 | 16 | public TreeSelectQuery(TreeTable callback) { 17 | super("trees", callback); 18 | } 19 | 20 | @Override 21 | public String getSQL() { 22 | return "SELECT * FROM trees WHERE sapling = true"; 23 | } 24 | 25 | @Override 26 | public List queryDatabase(Connection connection) throws SQLException { 27 | List treeList = new ArrayList<>(); 28 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 29 | 30 | while (rs.next()) { 31 | treeList.add(new Tree(rs)); 32 | } 33 | 34 | return treeList; 35 | } 36 | 37 | @Override 38 | public Statement prepareStatement(Connection connection) throws SQLException { 39 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/PlayerInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import lombok.Getter; 4 | import net.porillo.database.api.InsertQuery; 5 | import net.porillo.objects.GPlayer; 6 | 7 | import java.sql.Connection; 8 | import java.sql.PreparedStatement; 9 | import java.sql.SQLException; 10 | 11 | public class PlayerInsertQuery extends InsertQuery { 12 | 13 | @Getter 14 | private GPlayer player; 15 | 16 | public PlayerInsertQuery(GPlayer player) { 17 | super("players"); 18 | this.player = player; 19 | } 20 | 21 | @Override 22 | public String getSQL() { 23 | return "INSERT INTO players (uniqueId, uuid, firstSeen, carbonScore, worldId) VALUES (?,?,?,?,?)"; 24 | } 25 | 26 | @Override 27 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 28 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 29 | preparedStatement.setInt(1, player.getUniqueId()); 30 | preparedStatement.setString(2, player.getUuid().toString()); 31 | preparedStatement.setLong(3, player.getFirstSeen()); 32 | preparedStatement.setInt(4, player.getCarbonScore()); 33 | preparedStatement.setString(5, player.getWorldId().toString()); 34 | return preparedStatement; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/PlayerSelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.PlayerTable; 5 | import net.porillo.objects.GPlayer; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class PlayerSelectQuery extends SelectQuery { 15 | 16 | public PlayerSelectQuery(PlayerTable callback) { 17 | super("players", callback); 18 | } 19 | 20 | @Override 21 | public String getSQL() { 22 | return "SELECT * FROM players"; 23 | } 24 | 25 | @Override 26 | public List queryDatabase(Connection connection) throws SQLException { 27 | List gPlayers = new ArrayList<>(); 28 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 29 | 30 | while (rs.next()) { 31 | gPlayers.add(new GPlayer(rs)); 32 | } 33 | 34 | return gPlayers; 35 | } 36 | 37 | @Override 38 | public Statement prepareStatement(Connection connection) throws SQLException { 39 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/ReductionInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.Reduction; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class ReductionInsertQuery extends InsertQuery { 11 | 12 | private Reduction reduction; 13 | 14 | public ReductionInsertQuery(Reduction reduction) { 15 | super("reductions"); 16 | this.reduction = reduction; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO reductions (uniqueId, reductionerId, reductionKey, worldId, value) VALUES (?,?,?,?,?)"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, reduction.getUniqueID()); 28 | preparedStatement.setInt(2, reduction.getReductioner()); 29 | preparedStatement.setInt(3, reduction.getReductionKey()); 30 | preparedStatement.setString(4, reduction.getWorldId().toString()); 31 | preparedStatement.setInt(5, reduction.getReductionValue()); 32 | return preparedStatement; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/WorldInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.GWorld; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class WorldInsertQuery extends InsertQuery { 11 | 12 | private GWorld world; 13 | 14 | public WorldInsertQuery(GWorld world) { 15 | super("worlds"); 16 | this.world = world; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT IGNORE INTO worlds (uniqueId, worldId, firstSeen, carbonValue, seaLevel, size)" + 22 | " VALUES (?,?,?,?,?,?)"; 23 | } 24 | 25 | @Override 26 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 27 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 28 | preparedStatement.setInt(1, world.getUniqueID()); 29 | preparedStatement.setString(2, world.getWorldId().toString()); 30 | preparedStatement.setLong(3, world.getFirstSeen()); 31 | preparedStatement.setInt(4, world.getCarbonValue()); 32 | preparedStatement.setInt(5, world.getSeaLevel()); 33 | preparedStatement.setInt(6, world.getSize()); 34 | return preparedStatement; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/FurnaceSelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.FurnaceTable; 5 | import net.porillo.objects.Furnace; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class FurnaceSelectQuery extends SelectQuery { 15 | 16 | public FurnaceSelectQuery(FurnaceTable callback) { 17 | super("furnaces", callback); 18 | } 19 | 20 | @Override 21 | public List queryDatabase(Connection connection) throws SQLException { 22 | List furnaces = new ArrayList<>(); 23 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 24 | 25 | while (rs.next()) { 26 | furnaces.add(new Furnace(rs)); 27 | } 28 | 29 | return furnaces; 30 | } 31 | 32 | @Override 33 | public Statement prepareStatement(Connection connection) throws SQLException { 34 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 35 | } 36 | 37 | @Override 38 | public String getSQL() { 39 | return "SELECT * FROM furnaces WHERE active = true"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/EntityInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.TrackedEntity; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class EntityInsertQuery extends InsertQuery { 11 | 12 | private TrackedEntity entity; 13 | 14 | public EntityInsertQuery(TrackedEntity entity) { 15 | super("entities"); 16 | this.entity = entity; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO entities (uniqueId, uuid, breederId, entityType, ticksLived, alive) VALUES (?,?,?,?,?,?)"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, entity.getUniqueId()); 28 | preparedStatement.setString(2, entity.getUuid().toString()); 29 | preparedStatement.setInt(3, entity.getBreederId()); 30 | preparedStatement.setString(4, entity.getEntityType().name()); 31 | preparedStatement.setLong(5, entity.getTicksLived()); 32 | preparedStatement.setBoolean(6, entity.isAlive()); 33 | return preparedStatement; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/OffsetSelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.OffsetTable; 5 | import net.porillo.objects.OffsetBounty; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class OffsetSelectQuery extends SelectQuery { 15 | 16 | public OffsetSelectQuery(OffsetTable callback) { 17 | super("offsets", callback); 18 | } 19 | 20 | @Override 21 | public String getSQL() { 22 | return "SELECT * FROM offsets WHERE timeCompleted = 0"; 23 | } 24 | 25 | @Override 26 | public List queryDatabase(Connection connection) throws SQLException { 27 | List bounties = new ArrayList<>(); 28 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 29 | 30 | while (rs.next()) { 31 | bounties.add(new OffsetBounty(rs)); 32 | } 33 | 34 | return bounties; 35 | } 36 | 37 | @Override 38 | public Statement prepareStatement(Connection connection) throws SQLException { 39 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/ContributionInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.Contribution; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class ContributionInsertQuery extends InsertQuery { 11 | 12 | private Contribution contribution; 13 | 14 | public ContributionInsertQuery(Contribution contribution) { 15 | super("contributions"); 16 | this.contribution = contribution; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO contributions (uniqueId, contributerId, contributionKey, worldId, value) VALUES (?,?,?,?,?)"; 22 | } 23 | 24 | @Override 25 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 26 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 27 | preparedStatement.setInt(1, contribution.getUniqueID()); 28 | preparedStatement.setInt(2, contribution.getContributer()); 29 | preparedStatement.setInt(3, contribution.getContributionKey()); 30 | preparedStatement.setString(4, contribution.getWorldId().toString()); 31 | preparedStatement.setInt(5, contribution.getContributionValue()); 32 | return preparedStatement; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/select/EntitySelectQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.select; 2 | 3 | import net.porillo.database.api.SelectQuery; 4 | import net.porillo.database.tables.EntityTable; 5 | import net.porillo.objects.TrackedEntity; 6 | 7 | import java.sql.Connection; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class EntitySelectQuery extends SelectQuery { 15 | 16 | public EntitySelectQuery(EntityTable callback) { 17 | super("entities", callback); 18 | } 19 | 20 | @Override 21 | public List queryDatabase(Connection connection) throws SQLException { 22 | List trackedEntities = new ArrayList<>(); 23 | ResultSet rs = prepareStatement(connection).executeQuery(getSQL()); 24 | 25 | while (rs.next()) { 26 | trackedEntities.add(new TrackedEntity(rs)); 27 | } 28 | 29 | return trackedEntities; 30 | } 31 | 32 | @Override 33 | public Statement prepareStatement(Connection connection) throws SQLException { 34 | return connection.createStatement(); // H2 doesn't allow empty prepared statements 35 | } 36 | 37 | @Override 38 | public String getSQL() { 39 | return "SELECT * FROM entities WHERE alive = true"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/update/OffsetUpdateQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.update; 2 | 3 | import net.porillo.database.api.UpdateQuery; 4 | import net.porillo.objects.OffsetBounty; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class OffsetUpdateQuery extends UpdateQuery { 11 | 12 | public OffsetUpdateQuery(OffsetBounty offsetBounty) { 13 | super("offsets", offsetBounty); 14 | } 15 | 16 | @Override 17 | public String getSQL() { 18 | return "UPDATE offsets SET hunterId = ?, timeStarted = ?, logBlocksTarget = ?, timeCompleted = ? WHERE uniqueId = ?"; 19 | } 20 | 21 | @Override 22 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 23 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 24 | if (getObject().getHunterId() == null) { 25 | preparedStatement.setObject(1, null); 26 | } else { 27 | preparedStatement.setInt(1, getObject().getHunterId()); 28 | } 29 | 30 | preparedStatement.setLong(2, getObject().getTimeStarted()); 31 | preparedStatement.setLong(3, getObject().getLogBlocksTarget()); 32 | preparedStatement.setLong(4, getObject().getTimeCompleted()); 33 | preparedStatement.setInt(5, getObject().getUniqueId()); 34 | 35 | return preparedStatement; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/ModelTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo; 2 | 3 | import net.porillo.engine.models.*; 4 | import org.testng.annotations.Test; 5 | 6 | import static net.porillo.engine.models.ScoreTempModel.CarbonSensitivity; 7 | 8 | @Test 9 | public class ModelTest { 10 | 11 | private static final String world = "world"; 12 | 13 | @Test 14 | public void testMethaneModel() { 15 | EntityMethaneModel methaneModel = new EntityMethaneModel(world); 16 | } 17 | 18 | @Test 19 | public void testEntityFitnessModel() { 20 | EntityFitnessModel model = new EntityFitnessModel(world); 21 | } 22 | 23 | @Test 24 | public void testScoreTempModel() { 25 | ScoreTempModel model = new ScoreTempModel(world, CarbonSensitivity.LOW); 26 | 27 | // Test the interpolator 28 | // If any of the points aren't monotonically increasing, an exception is thrown 29 | for (int i = 1; i < 1000000; i += 1000) { 30 | model.getTemperature(i); 31 | } 32 | } 33 | 34 | public void testScoreIndexModel() { 35 | CarbonIndexModel model = new CarbonIndexModel(world); 36 | model.loadModel(); 37 | 38 | // Test the interpolator 39 | // If any of the points aren't monotonically increasing, an exception is thrown 40 | for (int i = -1000000; i < 1000000; i += 1000) { 41 | //System.out.println(String.format("%d - %4.3f", i, model.getCarbonIndex(i))); 42 | model.getCarbonIndex(i); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/FurnaceInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.Furnace; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class FurnaceInsertQuery extends InsertQuery { 11 | 12 | private Furnace furnace; 13 | 14 | public FurnaceInsertQuery(Furnace furnace) { 15 | super("furnaces"); 16 | this.furnace = furnace; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO furnaces (uniqueId, ownerId, worldId, blockX, blockY, blockZ, active) " + 22 | "VALUES (?,?,?,?,?,?,?)"; 23 | } 24 | 25 | @Override 26 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 27 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 28 | preparedStatement.setInt(1, furnace.getUniqueId()); 29 | preparedStatement.setInt(2, furnace.getOwnerId()); 30 | preparedStatement.setString(3, furnace.getLocation().getWorld().getUID().toString()); 31 | preparedStatement.setInt(4, furnace.getLocation().getBlockX()); 32 | preparedStatement.setInt(5, furnace.getLocation().getBlockY()); 33 | preparedStatement.setInt(6, furnace.getLocation().getBlockZ()); 34 | preparedStatement.setBoolean(7, furnace.isActive()); 35 | return preparedStatement; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/ReductionModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.Model; 9 | import org.bukkit.Material; 10 | 11 | import java.util.Map; 12 | 13 | public class ReductionModel extends Model { 14 | 15 | @Getter private Map reductionMap; 16 | private final Gson gson; 17 | 18 | public ReductionModel(Gson gson, String worldName) { 19 | super(worldName, "reductionModel.json"); 20 | this.gson = gson; 21 | this.loadModel(); 22 | } 23 | 24 | @Override 25 | public void loadModel() { 26 | this.reductionMap = gson.fromJson(super.getContents(),new TypeToken>() {}.getType()); 27 | 28 | if (this.reductionMap == null) { 29 | throw new RuntimeException(String.format("No values found in: [%s]", super.getPath())); 30 | } 31 | } 32 | 33 | public double getReduction(Material block) { 34 | if (reductionMap.containsKey(block)) { 35 | return reductionMap.get(block); 36 | } else { 37 | GlobalWarming.getInstance().getLogger().warning( 38 | String.format("No reduction defined in %s/reductionModel.json for: [%s]", 39 | getWorldName(), block.name())); 40 | return 0; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/TreeInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.Tree; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class TreeInsertQuery extends InsertQuery { 11 | 12 | private Tree tree; 13 | 14 | public TreeInsertQuery(Tree tree) { 15 | super("trees"); 16 | this.tree = tree; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO trees (uniqueId, ownerId, worldId, blockX, blockY, blockZ, sapling, size)" + 22 | " VALUES (?,?,?,?,?,?,?,?)"; 23 | } 24 | 25 | @Override 26 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 27 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 28 | preparedStatement.setInt(1, tree.getUniqueId()); 29 | preparedStatement.setInt(2, tree.getOwnerId()); 30 | preparedStatement.setString(3, tree.getLocation().getWorld().getUID().toString()); 31 | preparedStatement.setInt(4, tree.getLocation().getBlockX()); 32 | preparedStatement.setInt(5, tree.getLocation().getBlockY()); 33 | preparedStatement.setInt(6, tree.getLocation().getBlockZ()); 34 | preparedStatement.setBoolean(7, tree.isSapling()); 35 | preparedStatement.setInt(8, tree.getSize()); 36 | return preparedStatement; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queue/ConcurrentHashQueue.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queue; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | import java.util.Queue; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.ConcurrentLinkedQueue; 8 | 9 | public class ConcurrentHashQueue implements Iterable { 10 | 11 | private Map hashcodeMap; 12 | private Queue hashcodeQueue; 13 | 14 | public ConcurrentHashQueue() { 15 | this.hashcodeMap = new ConcurrentHashMap<>(); 16 | this.hashcodeQueue = new ConcurrentLinkedQueue<>(); 17 | } 18 | 19 | public void offer(T element) { 20 | if (!hashcodeMap.containsKey(element.hashCode())) { 21 | hashcodeQueue.offer(element.hashCode()); 22 | hashcodeMap.put(element.hashCode(), element); 23 | } 24 | } 25 | 26 | public T poll() { 27 | if (isEmpty()) return null; 28 | 29 | Integer hashcode = hashcodeQueue.poll(); 30 | return hashcodeMap.remove(hashcode); 31 | } 32 | 33 | public T peek() { 34 | if (isEmpty()) return null; 35 | 36 | Integer hashcode = hashcodeQueue.peek(); 37 | return hashcodeMap.get(hashcode); 38 | } 39 | 40 | public boolean isEmpty() { 41 | return hashcodeQueue.isEmpty(); 42 | } 43 | 44 | public int size() { 45 | return hashcodeQueue.size(); 46 | } 47 | 48 | @Override 49 | public Iterator iterator() { 50 | return hashcodeMap.values().iterator(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/api/Distribution.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.api; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class Distribution { 10 | 11 | public double[] temp; 12 | public double[] fitness; 13 | @Getter private transient List x, y; 14 | 15 | public Distribution(List x, List y) { 16 | this.x = x; 17 | this.y = y; 18 | } 19 | 20 | public Distribution(double[] temp, double[] fitness) { 21 | this.x = new ArrayList<>(); 22 | this.y = new ArrayList<>(); 23 | Arrays.stream(temp).forEach(d -> this.x.add(d)); 24 | Arrays.stream(fitness).forEach(d -> this.y.add(d)); 25 | } 26 | 27 | public double getValue(double input) { 28 | // Compatibility 29 | if (x == null || y == null) { 30 | this.x = new ArrayList<>(); 31 | this.y = new ArrayList<>(); 32 | Arrays.stream(temp).forEach(d -> this.x.add(d)); 33 | Arrays.stream(fitness).forEach(d -> this.y.add(d)); 34 | } 35 | 36 | if (input <= x.get(0)) { 37 | return y.get(0); 38 | } else if (input >= x.get(x.size() - 1)) { 39 | return y.get(y.size() - 1); 40 | } 41 | 42 | for (int i = 0; i < x.size(); i++) { 43 | if (input < x.get(i)) { 44 | return y.get(i - 1) + (input - x.get(i - 1)) / (x.get(i) - x.get(i - 1)) * (y.get(i) - y.get(i - 1)); 45 | } 46 | } 47 | return -1; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/TableManager.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import lombok.Getter; 4 | import net.porillo.database.tables.*; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | @Getter 10 | public class TableManager { 11 | 12 | private static TableManager instance; 13 | 14 | // Tables which load data from DB into memory 15 | private WorldTable worldTable; 16 | private PlayerTable playerTable; 17 | private FurnaceTable furnaceTable; 18 | private TreeTable treeTable; 19 | private OffsetTable offsetTable; 20 | private EntityTable entityTable; 21 | 22 | // Tables that currently don't load any data from the DB 23 | private ReductionTable reductionTable; 24 | private ContributionTable contributionTable; 25 | 26 | public TableManager() { 27 | this.worldTable = new WorldTable(); 28 | this.playerTable = new PlayerTable(); 29 | this.furnaceTable = new FurnaceTable(); 30 | this.treeTable = new TreeTable(); 31 | this.offsetTable = new OffsetTable(); 32 | this.entityTable = new EntityTable(); 33 | 34 | this.reductionTable = new ReductionTable(); 35 | this.contributionTable = new ContributionTable(); 36 | } 37 | 38 | public List getTables() { 39 | return Arrays.asList(worldTable, playerTable, furnaceTable, treeTable, offsetTable, entityTable); 40 | } 41 | 42 | public static TableManager getInstance() { 43 | if (instance == null) { 44 | instance = new TableManager(); 45 | } 46 | 47 | return instance; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/CarbonIndexModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.reflect.TypeToken; 4 | import lombok.Getter; 5 | import net.porillo.GlobalWarming; 6 | import net.porillo.engine.ClimateEngine; 7 | import net.porillo.engine.api.Distribution; 8 | import net.porillo.engine.api.Model; 9 | 10 | import java.util.Comparator; 11 | import java.util.Map; 12 | import java.util.TreeMap; 13 | 14 | public class CarbonIndexModel extends Model { 15 | 16 | @Getter private Map indexMap; 17 | private Distribution distribution; 18 | 19 | public CarbonIndexModel(String worldName) { 20 | super(worldName, "carbonIndexModel.json"); 21 | this.loadModel(); 22 | } 23 | 24 | @Override 25 | public void loadModel() { 26 | this.indexMap = new TreeMap<>(Comparator.naturalOrder()); 27 | this.indexMap.putAll(GlobalWarming.getInstance().getGson() 28 | .fromJson(super.getContents(), new TypeToken>() { 29 | }.getType())); 30 | 31 | double[] scores = new double[indexMap.size()]; 32 | double[] indices = new double[indexMap.size()]; 33 | 34 | int i = 0; 35 | for (Map.Entry entry : indexMap.entrySet()) { 36 | scores[i] = (double) entry.getKey(); 37 | indices[i++] = entry.getValue(); 38 | } 39 | 40 | this.distribution = new Distribution(scores, indices); 41 | } 42 | 43 | public double getCarbonIndex(int score) { 44 | return distribution.getValue((double) score); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/Reduction.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.UUID; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class Reduction { 13 | 14 | /** 15 | * Unique Id in DB created for this reduction 16 | */ 17 | private Integer uniqueID; 18 | 19 | /** 20 | * Unique Id of the Player who caused this reduction 21 | */ 22 | private Integer reductioner; 23 | 24 | /** 25 | * Unique Id of the associated object that corresponds to this reduction 26 | * - Warning: furnaces may be deleted over time (this value may become invalid) 27 | */ 28 | private Integer reductionKey; 29 | 30 | /** 31 | * UUID of the Bukkit world where the reduction took place 32 | */ 33 | private UUID worldId; 34 | 35 | /** 36 | * Calculated emissions reduction value 37 | */ 38 | private Integer reductionValue; 39 | 40 | /** 41 | * Number of blocks in the tree growth, if this reduction is a tree growth 42 | */ 43 | private Integer numBlocks; 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (o == null || getClass() != o.getClass()) return false; 49 | if (!super.equals(o)) return false; 50 | 51 | Reduction reduction = (Reduction) o; 52 | return uniqueID.equals(reduction.uniqueID); 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | int result = super.hashCode(); 58 | result = 31 * result + uniqueID.hashCode(); 59 | return result; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/EntityFitnessModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.JsonSyntaxException; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.MobDistribution; 9 | import net.porillo.engine.api.Model; 10 | import org.bukkit.entity.EntityType; 11 | 12 | import java.util.Map; 13 | 14 | public class EntityFitnessModel extends Model { 15 | 16 | @Getter private Map entityFitnessMap; 17 | 18 | public EntityFitnessModel(String worldName) { 19 | super(worldName, "entityFitnessModel.json"); 20 | this.loadModel(); 21 | } 22 | 23 | @Override 24 | public void loadModel() { 25 | try { 26 | this.entityFitnessMap = GlobalWarming.getInstance().getGson().fromJson( 27 | super.getContents(), 28 | new TypeToken>() { 29 | }.getType()); 30 | } catch (JsonSyntaxException ex) { 31 | ex.printStackTrace(); 32 | GlobalWarming.getInstance().getLogger().severe("Error loading model file: " + super.getPath()); 33 | GlobalWarming.getInstance().getLogger().severe("Could not load into the expected mapping."); 34 | GlobalWarming.getInstance().getLogger().severe("Please check the formatting and verify the types are correct."); 35 | return; 36 | } 37 | 38 | if (this.entityFitnessMap == null) { 39 | throw new RuntimeException(String.format("No values found in: [%s]", super.getPath())); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/queries/insert/OffsetInsertQuery.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.queries.insert; 2 | 3 | import net.porillo.database.api.InsertQuery; 4 | import net.porillo.objects.OffsetBounty; 5 | 6 | import java.sql.Connection; 7 | import java.sql.PreparedStatement; 8 | import java.sql.SQLException; 9 | 10 | public class OffsetInsertQuery extends InsertQuery { 11 | 12 | private OffsetBounty offsetBounty; 13 | 14 | public OffsetInsertQuery(OffsetBounty offsetBounty) { 15 | super("offsets"); 16 | this.offsetBounty = offsetBounty; 17 | } 18 | 19 | @Override 20 | public String getSQL() { 21 | return "INSERT INTO offsets (uniqueId, creatorId, hunterId, worldId, logBlocksTarget, reward, timeStarted, timeCompleted)" + 22 | " VALUES (?,?,?,?,?,?,?,?)"; 23 | } 24 | 25 | @Override 26 | public PreparedStatement prepareStatement(Connection connection) throws SQLException { 27 | PreparedStatement preparedStatement = connection.prepareStatement(getSQL()); 28 | preparedStatement.setInt(1, offsetBounty.getUniqueId()); 29 | preparedStatement.setInt(2, offsetBounty.getCreatorId()); 30 | 31 | if (offsetBounty.getHunterId() == null) { 32 | preparedStatement.setObject(3, null); 33 | } else { 34 | preparedStatement.setInt(3, offsetBounty.getHunterId()); 35 | } 36 | 37 | preparedStatement.setString(4, offsetBounty.getWorldId().toString()); 38 | preparedStatement.setInt(5, offsetBounty.getLogBlocksTarget()); 39 | preparedStatement.setInt(6, offsetBounty.getReward()); 40 | preparedStatement.setLong(7, offsetBounty.getTimeStarted()); 41 | preparedStatement.setLong(8, offsetBounty.getTimeCompleted()); 42 | return preparedStatement; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/TreeTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import net.porillo.GlobalWarming; 4 | import net.porillo.database.api.SelectCallback; 5 | import net.porillo.database.queries.delete.TreeDeleteQuery; 6 | import net.porillo.database.queries.select.TreeSelectQuery; 7 | import net.porillo.database.queue.AsyncDBQueue; 8 | import net.porillo.objects.Tree; 9 | import org.bukkit.Location; 10 | import org.bukkit.scheduler.BukkitRunnable; 11 | 12 | import java.util.List; 13 | 14 | public class TreeTable extends TrackedBlockTable implements SelectCallback { 15 | public TreeTable() { 16 | super("trees"); 17 | createIfNotExists(); 18 | TreeSelectQuery selectQuery = new TreeSelectQuery(this); 19 | AsyncDBQueue.getInstance().queueSelectQuery(selectQuery); 20 | } 21 | 22 | @Override 23 | public void onSelectionCompletion(List returnList) { 24 | if (GlobalWarming.getInstance() != null) { 25 | new BukkitRunnable() { 26 | @Override 27 | public void run() { 28 | for (Tree tree : returnList) { 29 | updateCollections(tree); 30 | } 31 | } 32 | }.runTask(GlobalWarming.getInstance()); 33 | } else { 34 | System.out.printf("Selection returned %d trees%n", returnList.size()); 35 | } 36 | } 37 | 38 | @Override 39 | public Tree deleteLocation(Location location) { 40 | Tree deletedTree = (Tree) super.deleteLocation(location); 41 | if (deletedTree != null) { 42 | TreeDeleteQuery deleteQuery = new TreeDeleteQuery(deletedTree); 43 | AsyncDBQueue.getInstance().queueDeleteQuery(deleteQuery); 44 | } 45 | 46 | return deletedTree; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/FuelModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonSyntaxException; 5 | import com.google.gson.reflect.TypeToken; 6 | import lombok.Getter; 7 | import net.porillo.GlobalWarming; 8 | import net.porillo.engine.api.Model; 9 | import org.bukkit.Material; 10 | 11 | import java.util.Map; 12 | 13 | public class FuelModel extends Model { 14 | 15 | @Getter private Map fuelMap; 16 | 17 | private final Gson gson; 18 | 19 | public FuelModel(Gson gson, String worldName) { 20 | super(worldName, "fuelModel.json"); 21 | this.gson = gson; 22 | this.loadModel(); 23 | } 24 | 25 | @Override 26 | public void loadModel() { 27 | try { 28 | this.fuelMap = gson.fromJson(super.getContents(), new TypeToken>() {}.getType()); 29 | } catch (JsonSyntaxException ex) { 30 | ex.printStackTrace(); 31 | GlobalWarming.getInstance().getLogger().severe("Error loading model file: " + super.getPath()); 32 | GlobalWarming.getInstance().getLogger().severe("Could not load into the expected mapping."); 33 | GlobalWarming.getInstance().getLogger().severe("Please check the formatting and verify the types are correct."); 34 | } 35 | 36 | if (this.fuelMap == null) { 37 | throw new RuntimeException(String.format("No values found in: [%s]", super.getPath())); 38 | } 39 | } 40 | 41 | public double getContribution(Material fuelType) { 42 | if (fuelMap.containsKey(fuelType)) { 43 | return fuelMap.get(fuelType); 44 | } else { 45 | throw new NullPointerException(String.format("No contribution defined in the model for: [%s]", fuelType.name())); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/negative/formation/IceForm.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.negative.formation; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.effect.api.ListenerClimateEffect; 10 | import net.porillo.engine.ClimateEngine; 11 | import net.porillo.engine.api.Distribution; 12 | import net.porillo.engine.api.WorldClimateEngine; 13 | import org.bukkit.Material; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.block.BlockFormEvent; 16 | 17 | @ClimateData(type = ClimateEffectType.ICE_FORMATION) 18 | public class IceForm extends ListenerClimateEffect { 19 | 20 | @Getter private Distribution heightMap; 21 | 22 | @EventHandler 23 | public void blockFormEvent(BlockFormEvent event) { 24 | if (event.getNewState().getType() == Material.ICE) { 25 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(event.getBlock().getWorld().getUID()); 26 | if (climateEngine != null && climateEngine.isEffectEnabled(ClimateEffectType.ICE_FORMATION)) { 27 | if (event.getBlock().getY() < heightMap.getValue(climateEngine.getTemperature())) { 28 | event.setCancelled(true); 29 | } 30 | } 31 | } 32 | } 33 | 34 | @Override 35 | public void setJsonModel(JsonObject jsonModel) { 36 | super.setJsonModel(jsonModel); 37 | this.heightMap = GlobalWarming.getInstance().getGson().fromJson( 38 | jsonModel, 39 | new TypeToken() { 40 | }.getType()); 41 | 42 | if (this.heightMap == null) { 43 | unregister(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/util/ChunkSorter.java: -------------------------------------------------------------------------------- 1 | package net.porillo.util; 2 | 3 | import net.porillo.objects.GChunk; 4 | import org.bukkit.Chunk; 5 | import org.bukkit.Location; 6 | import org.bukkit.entity.Player; 7 | 8 | import java.util.*; 9 | 10 | public class ChunkSorter { 11 | 12 | public static List sortByDistance(Chunk[] chunks, Map waterLevel, List players, int height, int numChunks) { 13 | if (players.size() == 0) { 14 | return Arrays.asList(chunks).subList(0, Math.min(numChunks, chunks.length)); 15 | } else { 16 | List sortedChunks = new ArrayList<>(); 17 | for (Chunk chunk : chunks) { 18 | GChunk gchunk = new GChunk(chunk); 19 | if (waterLevel.containsKey(gchunk)) { 20 | int seaLevel = waterLevel.get(gchunk); 21 | if (seaLevel == height) { 22 | continue; 23 | } 24 | } 25 | 26 | sortedChunks.add(chunk); 27 | } 28 | sortedChunks.sort((o1, o2) -> { 29 | Location l1 = chunkToLocation(o1); 30 | Location l2 = chunkToLocation(o2); 31 | double d1 = 0; 32 | double d2 = 0; 33 | for (Player player : players) { 34 | d1 += l1.distance(player.getLocation()); 35 | d2 += l2.distance(player.getLocation()); 36 | } 37 | d1 /= players.size(); 38 | d2 /= players.size(); 39 | 40 | return Double.compare(d1, d2); 41 | }); 42 | 43 | return sortedChunks.subList(0, Math.min(numChunks, sortedChunks.size())); 44 | } 45 | } 46 | 47 | private static Location chunkToLocation(Chunk chunk) { 48 | return chunk.getBlock(8, 64, 8).getLocation(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/FurnaceTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import net.porillo.GlobalWarming; 4 | import net.porillo.database.api.SelectCallback; 5 | import net.porillo.database.queries.delete.FurnaceDeleteQuery; 6 | import net.porillo.database.queries.select.FurnaceSelectQuery; 7 | import net.porillo.database.queue.AsyncDBQueue; 8 | import net.porillo.objects.Furnace; 9 | import org.bukkit.Location; 10 | import org.bukkit.scheduler.BukkitRunnable; 11 | 12 | import java.util.List; 13 | 14 | public class FurnaceTable extends TrackedBlockTable implements SelectCallback { 15 | 16 | public FurnaceTable() { 17 | super("furnaces"); 18 | createIfNotExists(); 19 | 20 | FurnaceSelectQuery selectQuery = new FurnaceSelectQuery(this); 21 | AsyncDBQueue.getInstance().queueSelectQuery(selectQuery); 22 | } 23 | 24 | @Override 25 | public void onSelectionCompletion(List returnList) { 26 | if (GlobalWarming.getInstance() != null) { 27 | new BukkitRunnable() { 28 | @Override 29 | public void run() { 30 | for (Furnace furnace : returnList) { 31 | updateCollections(furnace); 32 | } 33 | } 34 | }.runTask(GlobalWarming.getInstance()); 35 | } else { 36 | System.out.printf("Selection returned %d furnaces.%n", returnList.size()); 37 | } 38 | } 39 | 40 | @Override 41 | public Furnace deleteLocation(Location location) { 42 | Furnace deletedFurnace = (Furnace) super.deleteLocation(location); 43 | if (deletedFurnace != null) { 44 | FurnaceDeleteQuery deleteQuery = new FurnaceDeleteQuery(deletedFurnace); 45 | AsyncDBQueue.getInstance().queueDeleteQuery(deleteQuery); 46 | } 47 | 48 | return deletedFurnace; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/negative/formation/SnowForm.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.negative.formation; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.effect.api.ListenerClimateEffect; 10 | import net.porillo.engine.ClimateEngine; 11 | import net.porillo.engine.api.Distribution; 12 | import net.porillo.engine.api.WorldClimateEngine; 13 | import org.bukkit.Material; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.block.BlockFormEvent; 16 | 17 | @ClimateData(type = ClimateEffectType.SNOW_FORMATION) 18 | public class SnowForm extends ListenerClimateEffect { 19 | 20 | @Getter private Distribution heightMap; 21 | 22 | @EventHandler 23 | public void blockFormEvent(BlockFormEvent event) { 24 | if (event.getNewState().getType() == Material.SNOW) { 25 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(event.getBlock().getWorld().getUID()); 26 | if (climateEngine != null && climateEngine.isEffectEnabled(ClimateEffectType.SNOW_FORMATION)) { 27 | double temperature = climateEngine.getTemperature(); 28 | if (event.getBlock().getY() < heightMap.getValue(temperature)) { 29 | event.setCancelled(true); 30 | } 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | public void setJsonModel(JsonObject jsonModel) { 37 | super.setJsonModel(jsonModel); 38 | this.heightMap = GlobalWarming.getInstance().getGson().fromJson( 39 | jsonModel, 40 | new TypeToken() { 41 | }.getType()); 42 | 43 | if (this.heightMap == null) { 44 | unregister(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #-------------------------------------------------------------------------------- 3 | # BUILD GLOBALWARMING (UBUNTU) 4 | # 5 | # SYNOPSIS 6 | # . launch.sh [CLEAN] 7 | # 8 | # DESCRIPTION 9 | # Build and deploy the GlobalWarming plugin 10 | # 11 | # CLEAN 12 | # Rebuilds the database and clears the plugin config without deleting the 13 | # world 14 | # 15 | # NOTES 16 | # This script expects the working directory to be the GlobalWarming directory. 17 | # Consider running setup.sh first to install the required dependencies and 18 | # then run this file with the CLEAN option to reset the database, etc. (if you 19 | # are using a different branch). 20 | #-------------------------------------------------------------------------------- 21 | 22 | LPAREN="\e[0m[\e[1;94m" 23 | RPAREN="\e[21;0m]" 24 | echo -e "${LPAREN}BUILDING${RPAREN} (CTRL+C TO EXIT)" && 25 | mvn clean compile install && 26 | if [[ $1 == CLEAN ]]; then 27 | echo -e "${LPAREN}DELETING THE PLUGIN CONFIGURATION${RPAREN}" && 28 | sudo rm -rf /home/minecraft/server/plugins/GlobalWarming/* && 29 | echo -e "${LPAREN}DROPPING THE DATABASE AND USER${RPAREN}" && 30 | sudo mysql -u root -Bse "DROP DATABASE IF EXISTS GlobalWarming;DROP USER IF EXISTS 'user'@'localhost';" && 31 | echo -e "${LPAREN}CREATING THE DATABASE, USER AND PERMISSIONS${RPAREN}" && 32 | sudo mysql -u root -Bse "CREATE DATABASE GlobalWarming;USE GlobalWarming;CREATE USER 'user'@'localhost' IDENTIFIED BY 'pass'; GRANT ALL PRIVILEGES ON GlobalWarming.* TO 'user'@'localhost';" 33 | fi && 34 | echo -e "${LPAREN}COPYING THE GLOBALWARMING PLUGIN${RPAREN}" && 35 | sudo cp target/GlobalWarming.jar /home/minecraft/server/plugins/ && 36 | echo -e "${LPAREN}COPYING THE DATAPACK (CUSTOM ADVANCEMENTS)${RPAREN}" && 37 | sudo su minecraft -c "cp -r gw_datapack /home/minecraft/server/world/datapacks/" && 38 | echo -e "${LPAREN}LAUNCHING THE SERVER${RPAREN}" && 39 | sudo su minecraft -c "/home/minecraft/server/launch.sh" -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/green_thumbs_up.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "Green Thumbs Up" 5 | }, 6 | "description": { 7 | "text": "Plant a sapling. Trivia: trees are unique in their ability to lock up large amounts of carbon in their wood, and continue to add carbon as they grow [EPA, 2016]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:birch_sapling" 11 | }, 12 | "frame": "goal", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "requirements": [ 18 | [ 19 | "green_thumb_oak", 20 | "green_thumb_birch", 21 | "green_thumb_spruce", 22 | "green_thumb_jungle", 23 | "green_thumb_acacia", 24 | "green_thumb_dark_oak" 25 | ] 26 | ], 27 | "rewards": { 28 | "experience": 5 29 | }, 30 | "criteria": { 31 | "green_thumb_oak": { 32 | "trigger": "minecraft:placed_block", 33 | "conditions": { 34 | "block": "minecraft:oak_sapling" 35 | } 36 | }, 37 | "green_thumb_birch": { 38 | "trigger": "minecraft:placed_block", 39 | "conditions": { 40 | "block": "minecraft:birch_sapling" 41 | } 42 | }, 43 | "green_thumb_spruce": { 44 | "trigger": "minecraft:placed_block", 45 | "conditions": { 46 | "block": "minecraft:spruce_sapling" 47 | } 48 | }, 49 | "green_thumb_jungle": { 50 | "trigger": "minecraft:placed_block", 51 | "conditions": { 52 | "block": "minecraft:jungle_sapling" 53 | } 54 | }, 55 | "green_thumb_acacia": { 56 | "trigger": "minecraft:placed_block", 57 | "conditions": { 58 | "block": "minecraft:acacia_sapling" 59 | } 60 | }, 61 | "green_thumb_dark_oak": { 62 | "trigger": "minecraft:placed_block", 63 | "conditions": { 64 | "block": "minecraft:dark_oak_sapling" 65 | } 66 | } 67 | }, 68 | "parent": "gw:root" 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/Contribution.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.util.UUID; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class Contribution { 15 | 16 | /** 17 | * Random UUID created for this contribution 18 | */ 19 | private Integer uniqueID; 20 | 21 | /** 22 | * UUID of the Player who caused this contribution 23 | */ 24 | private Integer contributer; 25 | 26 | /** 27 | * UUID of the furnace that corresponds to this emission 28 | * - Warning: furnaces may be deleted over time (this value may become invalid) 29 | */ 30 | private Integer contributionKey; 31 | 32 | /** 33 | * UUID of the Bukkit world where the contribution took place 34 | */ 35 | private UUID worldId; 36 | 37 | /** 38 | * Calculated emissions value for this contribution 39 | */ 40 | private Integer contributionValue; 41 | 42 | public Contribution(ResultSet rs) throws SQLException { 43 | this.uniqueID = rs.getInt(1); 44 | this.contributer = rs.getInt(2); 45 | this.contributionKey = rs.getInt(3); 46 | this.worldId = UUID.fromString(rs.getString(4)); 47 | this.contributionValue = rs.getInt(6); 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) return true; 53 | if (o == null || getClass() != o.getClass()) return false; 54 | if (!super.equals(o)) return false; 55 | 56 | Contribution that = (Contribution) o; 57 | return uniqueID.equals(that.uniqueID); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | int result = super.hashCode(); 63 | result = 31 * result + uniqueID.hashCode(); 64 | return result; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/Furnace.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import net.porillo.GlobalWarming; 6 | import net.porillo.database.tables.PlayerTable; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.Location; 9 | 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | import java.util.UUID; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | public class Furnace extends TrackedBlock { 17 | 18 | /** 19 | * If this furnace currently exists in the world 20 | */ 21 | private boolean active; 22 | 23 | public Furnace(Integer uniqueId, Integer ownerId, Location location, boolean active) { 24 | super(uniqueId, ownerId, location); 25 | this.active = active; 26 | } 27 | 28 | public Furnace(ResultSet rs) throws SQLException { 29 | super(rs.getInt(1), 30 | rs.getInt(2), 31 | Bukkit.getWorld(UUID.fromString(rs.getString(3))) 32 | .getBlockAt(rs.getInt(4), rs.getInt(5), rs.getInt(6)).getLocation()); 33 | 34 | this.active = rs.getBoolean(7); 35 | } 36 | 37 | public GPlayer getOwner() { 38 | PlayerTable playerTable = GlobalWarming.getInstance().getTableManager().getPlayerTable(); 39 | UUID ownerUUID = playerTable.getUuidMap().get(getOwnerId()); 40 | return playerTable.getPlayers().get(ownerUUID); 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (o == null || getClass() != o.getClass()) return false; 47 | if (!super.equals(o)) return false; 48 | 49 | Furnace furnace = (Furnace) o; 50 | return getUniqueId().equals(furnace.getUniqueId()); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int result = super.hashCode(); 56 | result = 31 * result + getUniqueId().hashCode(); 57 | return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/EntityMethaneModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.JsonSyntaxException; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.Model; 9 | import org.bukkit.entity.EntityType; 10 | 11 | import java.util.Map; 12 | 13 | public class EntityMethaneModel extends Model { 14 | 15 | @Getter private Map entityMethaneMap; 16 | 17 | public EntityMethaneModel(String worldName) { 18 | super(worldName, "entityMethaneModel.json"); 19 | this.loadModel(); 20 | } 21 | 22 | @Override 23 | public void loadModel() { 24 | try { 25 | this.entityMethaneMap = GlobalWarming.getInstance().getGson().fromJson( 26 | super.getContents(), 27 | new TypeToken>() { 28 | }.getType()); 29 | } catch (JsonSyntaxException ex) { 30 | ex.printStackTrace(); 31 | GlobalWarming.getInstance().getLogger().severe("Error loading model file: " + super.getPath()); 32 | GlobalWarming.getInstance().getLogger().severe("Could not load into the expected mapping."); 33 | GlobalWarming.getInstance().getLogger().severe("Please check the formatting and verify the types are correct."); 34 | } 35 | 36 | if (this.entityMethaneMap == null) { 37 | throw new RuntimeException(String.format("No values found in: [%s]", super.getPath())); 38 | } 39 | } 40 | 41 | public double getContribution(EntityType entityType) { 42 | if (entityMethaneMap.containsKey(entityType)) { 43 | return entityMethaneMap.get(entityType); 44 | } else { 45 | GlobalWarming.getInstance().getLogger().info(String.format("No contribution defined in the model for: [%s]", entityType.name())); 46 | return 0.0; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/Tree.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.database.tables.PlayerTable; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.Location; 10 | 11 | import java.sql.ResultSet; 12 | import java.sql.SQLException; 13 | import java.util.UUID; 14 | 15 | @Data 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class Tree extends TrackedBlock { 19 | 20 | //TODO: Add JavaDocs 21 | private boolean isSapling; 22 | private Integer size; 23 | 24 | public Tree(Integer uniqueId, Integer ownerId, Location location, boolean isSapling, Integer size) { 25 | super(uniqueId, ownerId, location); 26 | this.isSapling = isSapling; 27 | this.size = size; 28 | } 29 | 30 | public Tree(ResultSet rs) throws SQLException { 31 | super(rs.getInt(1), 32 | rs.getInt(2), 33 | Bukkit.getWorld(UUID.fromString(rs.getString(3))) 34 | .getBlockAt(rs.getInt(4), rs.getInt(5), rs.getInt(6)).getLocation()); 35 | 36 | this.isSapling = rs.getBoolean(7); 37 | this.size = rs.getInt(8); 38 | } 39 | 40 | public GPlayer getOwner() { 41 | PlayerTable playerTable = GlobalWarming.getInstance().getTableManager().getPlayerTable(); 42 | UUID ownerUUID = playerTable.getUuidMap().get(getOwnerId()); 43 | return playerTable.getPlayers().get(ownerUUID); 44 | } 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (this == o) return true; 49 | if (o == null || getClass() != o.getClass()) return false; 50 | if (!super.equals(o)) return false; 51 | 52 | Tree tree = (Tree) o; 53 | return getUniqueId().equals(tree.getUniqueId()); 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | int result = super.hashCode(); 59 | result = 31 * result + getUniqueId().hashCode(); 60 | return result; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/database/TestUtility.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import lombok.Getter; 4 | import net.porillo.objects.Furnace; 5 | import net.porillo.objects.GPlayer; 6 | import net.porillo.objects.GWorld; 7 | 8 | import java.util.Random; 9 | import java.util.UUID; 10 | 11 | public class TestUtility { 12 | 13 | private static TestUtility instance; 14 | 15 | @Getter 16 | private final ConnectionManager connectionManager; 17 | @Getter 18 | private final Random random = new Random(); 19 | 20 | public TestUtility() { 21 | this.connectionManager = new ConnectionManager("mysql", "localhost", 3306, "GlobalWarming", "jenkins", "tests"); 22 | } 23 | 24 | public GWorld nextRandomWorld() { 25 | GWorld gWorld = new GWorld(); 26 | gWorld.setUniqueID(random.nextInt(Integer.MAX_VALUE)); 27 | gWorld.setWorldId(UUID.randomUUID()); 28 | gWorld.setFirstSeen(random.nextLong()); 29 | gWorld.setCarbonValue(random.nextInt(8388607)); 30 | gWorld.setSeaLevel(random.nextInt(255)); 31 | gWorld.setSize(random.nextInt(65535)); 32 | return gWorld; 33 | } 34 | 35 | public GPlayer nextRandomPlayer() { 36 | GPlayer gPlayer = new GPlayer(); 37 | gPlayer.setUniqueId(random.nextInt(Integer.MAX_VALUE)); 38 | gPlayer.setUuid(UUID.randomUUID()); 39 | gPlayer.setCarbonScore(random.nextInt(8388607)); 40 | gPlayer.setFirstSeen(0); 41 | gPlayer.setWorldId(UUID.randomUUID()); 42 | return gPlayer; 43 | } 44 | 45 | public Furnace nextRandomFurnace() { 46 | Furnace furnace = new Furnace(); 47 | furnace.setUniqueId(random.nextInt(Integer.MAX_VALUE)); 48 | furnace.setOwnerId(random.nextInt(Integer.MAX_VALUE)); 49 | //TODO: Update furnace class to use worldId,x,y,z for location 50 | return furnace; 51 | } 52 | 53 | public static TestUtility getInstance() { 54 | if (instance == null) { 55 | instance = new TestUtility(); 56 | } 57 | return instance; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/neutral/FarmYield.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.neutral; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.effect.api.ListenerClimateEffect; 10 | import net.porillo.engine.ClimateEngine; 11 | import net.porillo.engine.api.Distribution; 12 | import net.porillo.engine.api.WorldClimateEngine; 13 | import org.bukkit.Material; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.block.BlockGrowEvent; 16 | 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @ClimateData(type = ClimateEffectType.FARM_YIELD) 21 | public class FarmYield extends ListenerClimateEffect { 22 | 23 | @Getter private HashMap cropDistribution; 24 | 25 | @EventHandler 26 | public void onCropGrow(BlockGrowEvent event) { 27 | WorldClimateEngine worldEngine = ClimateEngine.getInstance().getClimateEngine(event.getBlock().getWorld().getUID()); 28 | if (worldEngine != null && worldEngine.isEffectEnabled(ClimateEffectType.FARM_YIELD)) { 29 | Distribution distribution = cropDistribution.get(event.getBlock().getType()); 30 | if (distribution != null) { 31 | double random = GlobalWarming.getInstance().getRandom().nextDouble(); 32 | double chance = distribution.getValue(worldEngine.getTemperature()); 33 | if (chance / 100.f <= random) { 34 | event.setCancelled(true); 35 | } 36 | } 37 | } 38 | } 39 | 40 | @Override 41 | public void setJsonModel(JsonObject jsonModel) { 42 | super.setJsonModel(jsonModel); 43 | this.cropDistribution = GlobalWarming.getInstance().getGson().fromJson( 44 | jsonModel, 45 | new TypeToken>() { 46 | }.getType()); 47 | 48 | if (cropDistribution == null) { 49 | unregister(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/database/TableCreationTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import net.porillo.database.queue.AsyncDBQueue; 4 | import net.porillo.database.tables.Table; 5 | import org.testng.annotations.BeforeClass; 6 | import org.testng.annotations.Test; 7 | 8 | import java.sql.Connection; 9 | import java.sql.SQLException; 10 | 11 | public class TableCreationTest { 12 | 13 | @BeforeClass 14 | public void dropTables() { 15 | dropTable("players"); 16 | dropTable("worlds"); 17 | dropTable("furnaces"); 18 | dropTable("trees"); 19 | dropTable("contributions"); 20 | dropTable("reductions"); 21 | dropTable("offsets"); 22 | } 23 | 24 | @Test(priority = 2) 25 | public void testTableCreation() throws SQLException, ClassNotFoundException { 26 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 27 | for (Table table : TableManager.getInstance().getTables()) { 28 | System.out.println("Testing table create for " + table.getTableName()); 29 | table.createIfNotExists(); 30 | } 31 | 32 | AsyncDBQueue.getInstance().writeCreateTableQueue(connection); 33 | 34 | tableAssertions("players"); 35 | tableAssertions("worlds"); 36 | tableAssertions("furnaces"); 37 | tableAssertions("trees"); 38 | tableAssertions("contributions"); 39 | tableAssertions("reductions"); 40 | tableAssertions("offsets"); 41 | } 42 | 43 | private void dropTable(String table) { 44 | try { 45 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 46 | connection.createStatement().executeUpdate(String.format("DROP TABLE IF EXISTS %s", table)); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | private void tableAssertions(String table) throws SQLException, ClassNotFoundException { 53 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 54 | 55 | // Verify the table is created, if the query fails then the table does not exist 56 | connection.createStatement().execute(String.format("SELECT 1 FROM %s LIMIT 1", table)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/EffectModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonPrimitive; 5 | import com.google.gson.reflect.TypeToken; 6 | import lombok.Getter; 7 | import net.porillo.GlobalWarming; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.engine.api.Model; 10 | import org.bukkit.Bukkit; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class EffectModel extends Model { 16 | 17 | @Getter private Map effectMap; 18 | 19 | public EffectModel() { 20 | super(Bukkit.getWorlds().get(0).getName(), "effectModel.json"); 21 | this.loadModel(); 22 | } 23 | 24 | @Override 25 | public void loadModel() { 26 | this.effectMap = GlobalWarming.getInstance().getGson() 27 | .fromJson(super.getContents(), new TypeToken>() { 28 | }.getType()); 29 | 30 | if (this.effectMap == null) { 31 | throw new RuntimeException(String.format("No values found in: [%s]", super.getPath())); 32 | } 33 | } 34 | 35 | public boolean isEnabled(ClimateEffectType effectType) { 36 | JsonObject effect = effectMap.get(effectType); 37 | if (effect == null) { 38 | return false; 39 | } else { 40 | JsonPrimitive enabled = effect.getAsJsonPrimitive("enabled"); 41 | if (enabled.isBoolean()) { 42 | return enabled.getAsBoolean(); 43 | } else { 44 | return false; 45 | } 46 | } 47 | } 48 | 49 | public JsonObject getEffect(ClimateEffectType effectType) { 50 | if (isEnabled(effectType)) { 51 | return effectMap.get(effectType); 52 | } else { 53 | return new JsonObject(); 54 | } 55 | } 56 | 57 | public HashMap getEffects() { 58 | HashMap effects = new HashMap<>(); 59 | for (Map.Entry entry : effectMap.entrySet()) { 60 | if (isEnabled(entry.getKey())) { 61 | effects.put(entry.getKey(), entry.getValue()); 62 | } 63 | } 64 | return effects; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/storage/EffectData.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.storage; 2 | 3 | import net.porillo.GlobalWarming; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | public class EffectData { 10 | 11 | private final String worldName; 12 | private final String effectName; 13 | private Path effectPath; 14 | 15 | public EffectData(String worldName, String effectName) { 16 | this.worldName = worldName; 17 | this.effectName = effectName; 18 | this.effectPath = GlobalWarming.getInstance().getDataFolder().toPath().resolve("effects"); 19 | } 20 | 21 | public String getEffectName() { 22 | return effectName; 23 | } 24 | 25 | public Path getPath() { 26 | return this.effectPath.resolve(worldName).resolve(effectName); 27 | } 28 | 29 | public String getContents() { 30 | createIfNotExists(); 31 | 32 | try { 33 | return new String(Files.readAllBytes(getPath())); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | return ""; 39 | } 40 | 41 | public void writeContents(String data) { 42 | clearFileForNewWrite(); 43 | 44 | try { 45 | Files.write(getPath(), data.getBytes()); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | 51 | private void createIfNotExists() { 52 | Path path = getPath(); 53 | if (!Files.exists(path)) { 54 | GlobalWarming.getInstance().getLogger().info( 55 | String.format("EffectData: [%s] does not exist at: [%s], creating.", effectName, path)); 56 | 57 | try { 58 | Files.createDirectories(effectPath.resolve(worldName)); 59 | Files.createFile(path); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | } 65 | 66 | private void clearFileForNewWrite() { 67 | Path file = getPath(); 68 | try { 69 | if (Files.exists(file)) { 70 | Files.delete(file); 71 | Files.createFile(file); 72 | } else { 73 | Files.createFile(file); 74 | } 75 | } catch (IOException ex) { 76 | ex.printStackTrace(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/papi/TemperatureExpansion.java: -------------------------------------------------------------------------------- 1 | package net.porillo.papi; 2 | 3 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.database.tables.PlayerTable; 6 | import net.porillo.database.tables.WorldTable; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.WorldClimateEngine; 9 | import net.porillo.objects.GPlayer; 10 | import net.porillo.objects.GWorld; 11 | import net.porillo.util.Colorizer; 12 | import org.bukkit.entity.Player; 13 | 14 | public class TemperatureExpansion extends PlaceholderExpansion { 15 | 16 | @Override 17 | public boolean canRegister() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public boolean register() { 23 | GlobalWarming.getInstance().getLogger().info("Temperature Expansion Placeholder Expansion loaded"); 24 | return super.register(); 25 | } 26 | 27 | @Override 28 | public String getIdentifier() { 29 | return "GlobalWarming"; 30 | } 31 | 32 | @Override 33 | public String getAuthor() { 34 | return "milkywayz"; 35 | } 36 | 37 | @Override 38 | public String getVersion() { 39 | return "1.0.0"; 40 | } 41 | 42 | @Override 43 | public String onPlaceholderRequest(Player p, String identifier) { 44 | if (p != null && p.isOnline()) { 45 | if (identifier.equalsIgnoreCase("world_temp")) { 46 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(p.getWorld().getUID()); 47 | return Colorizer.formatTemp(climateEngine.getTemperature()); 48 | } else if (identifier.equalsIgnoreCase("world_score")) { 49 | WorldTable worldTable = GlobalWarming.getInstance().getTableManager().getWorldTable(); 50 | GWorld gWorld = worldTable.getWorld(p.getWorld().getUID()); 51 | return String.valueOf(gWorld.getCarbonValue()); 52 | } else if (identifier.equalsIgnoreCase("player_score")) { 53 | PlayerTable playerTable = GlobalWarming.getInstance().getTableManager().getPlayerTable(); 54 | GPlayer gPlayer = playerTable.getPlayers().get(p.getUniqueId()); 55 | if (gPlayer != null) { 56 | return Colorizer.formatScore(gPlayer.getCarbonScore()); 57 | } 58 | } 59 | } 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/config/GlobalWarmingConfig.java: -------------------------------------------------------------------------------- 1 | package net.porillo.config; 2 | 3 | import lombok.Getter; 4 | import net.porillo.database.ConnectionManager; 5 | 6 | @Getter 7 | public class GlobalWarmingConfig extends ConfigLoader { 8 | 9 | private String host; 10 | private int port; 11 | private String database, type; 12 | private String username, password; 13 | private int maxBounties; 14 | private int chatTableWidth; 15 | private double degreesUntilChangeDetected; 16 | private int spamInterval; 17 | private int databaseInterval; 18 | private int notificationInterval; 19 | private int notificationDuration; 20 | private boolean scoreboardEnabled; 21 | private int scoreboardInterval; 22 | private String temperatureFormat; 23 | private boolean welcomingOnJoin; 24 | 25 | public GlobalWarmingConfig() { 26 | super("config.yml"); 27 | super.saveIfNotExist(); 28 | super.load(); 29 | } 30 | 31 | @Override 32 | protected void loadKeys() { 33 | this.chatTableWidth = conf.getInt("chat.table-width", 280); 34 | this.maxBounties = conf.getInt("bounty.max-created-per-player", 5); 35 | this.degreesUntilChangeDetected = conf.getDouble("climate-notification.degrees-until-change-detected", 0.25); 36 | this.spamInterval = conf.getInt("commands.spam-interval", 60); 37 | 38 | this.host = conf.getString("database.host"); 39 | this.port = conf.getInt("database.port"); 40 | this.database = conf.getString("database.name"); 41 | this.type = conf.getString("database.type", "H2"); 42 | this.username = conf.getString("database.username"); 43 | this.password = conf.getString("database.password"); 44 | this.databaseInterval = conf.getInt("database.interval", 300); 45 | 46 | this.notificationInterval = conf.getInt("notification.interval", 6000); 47 | this.notificationDuration = conf.getInt("notification.duration", 300); 48 | 49 | this.scoreboardEnabled = conf.getBoolean("scoreboard.enabled", true); 50 | this.scoreboardInterval = conf.getInt("scoreboard.interval", 20); 51 | this.temperatureFormat = conf.getString("temperature.format", "#.##"); 52 | 53 | this.welcomingOnJoin = conf.getBoolean("chat.welcome-on-join", true); 54 | } 55 | 56 | public ConnectionManager makeConnectionManager() { 57 | return new ConnectionManager(type, host, port, database, username, password); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/GWorld.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.util.UUID; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class GWorld { 15 | 16 | /** 17 | * Unique ID in database 18 | */ 19 | private Integer uniqueID; 20 | 21 | /** 22 | * Bukkit world ID 23 | */ 24 | private UUID worldId; 25 | 26 | /** 27 | * When we loaded this world into GlobalWarming. 28 | * This might be useful one day to normalize the rate of temperature 29 | * change so that worlds don't implode if a lot of CO2 is emitted. 30 | *

31 | * In reality, changes take some time to be realized in the world. 32 | */ 33 | private long firstSeen; 34 | 35 | /** 36 | * Number of chunks that have ever been loaded in this world 37 | * We increment on ChunkPopulateEvent, which only happens once 38 | * for every chunk. 39 | *

40 | * We want to incorporate size into the equation somehow, since 41 | * a tiny world with lots of furnaces and no trees should feel 42 | * the impact. 43 | */ 44 | private Integer size; 45 | 46 | /** 47 | * Numerical value representing the total amount of carbon 48 | * in the worlds atmosphere. Initially 0 49 | */ 50 | private Integer carbonValue; 51 | 52 | /** 53 | * The y coordinate which represents the current world sea level 54 | * Changes based on the effects of climate change 55 | */ 56 | private Integer seaLevel; 57 | 58 | public GWorld(ResultSet rs) throws SQLException { 59 | this.uniqueID = rs.getInt(1); 60 | this.worldId = UUID.fromString(rs.getString(2)); 61 | this.firstSeen = rs.getLong(3); 62 | this.carbonValue = rs.getInt(4); 63 | this.seaLevel = rs.getInt(5); 64 | this.size = rs.getInt(6); 65 | } 66 | 67 | @Override 68 | public boolean equals(Object o) { 69 | if (this == o) return true; 70 | if (o == null || getClass() != o.getClass()) return false; 71 | if (!super.equals(o)) return false; 72 | 73 | GWorld gWorld = (GWorld) o; 74 | return uniqueID.equals(gWorld.uniqueID); 75 | } 76 | 77 | @Override 78 | public int hashCode() { 79 | int result = super.hashCode(); 80 | result = 31 * result + uniqueID.hashCode(); 81 | return result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/ConnectionManager.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import java.io.File; 4 | import java.sql.Connection; 5 | import java.sql.DriverManager; 6 | import java.sql.SQLException; 7 | 8 | 9 | public class ConnectionManager { 10 | 11 | private Connection connection; 12 | private String host; 13 | private int port; 14 | private String database, type; 15 | private String username, password; 16 | 17 | public ConnectionManager(String type, String host, int port, String database, String username, String password) { 18 | this.type = type; 19 | this.host = host; 20 | this.port = port; 21 | this.database = database; 22 | this.username = username; 23 | this.password = password; 24 | } 25 | 26 | public Connection openConnection() throws SQLException, ClassNotFoundException { 27 | if (connection != null && !connection.isClosed()) { 28 | return connection; 29 | } 30 | 31 | synchronized (this) { 32 | if (connection != null && !connection.isClosed()) { 33 | return connection; 34 | } 35 | 36 | if (type.equalsIgnoreCase("H2")) { 37 | Class.forName("org.h2.Driver"); 38 | String path = String.format("%s/plugins/GlobalWarming/database", new File(".").getAbsolutePath()); 39 | String jdbcString = "jdbc:h2:file:" + path + ";MODE=MySQL;DB_CLOSE_ON_EXIT=TRUE;IGNORECASE=TRUE"; 40 | return DriverManager.getConnection(jdbcString, username, password); 41 | } else if (type.equalsIgnoreCase("MYSQL")) { 42 | Class.forName("com.mysql.jdbc.Driver"); 43 | String connectionString = String.format( 44 | "jdbc:mysql://%s:%d/%s?user=%s&password=%s&allowPublicKeyRetrieval=true&autoReconnect=true&useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC", 45 | this.host, 46 | this.port, 47 | this.database, 48 | this.username, 49 | this.password); 50 | 51 | connection = DriverManager.getConnection(connectionString); 52 | } 53 | 54 | return connection; 55 | } 56 | } 57 | 58 | public void close() { 59 | try { 60 | if (connection != null && !connection.isClosed()) { 61 | connection.close(); 62 | } 63 | } catch (Exception ex) { 64 | ex.printStackTrace(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/negative/PermanentSlowness.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.negative; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.Getter; 5 | import net.porillo.effect.ClimateData; 6 | import net.porillo.effect.api.ClimateEffectType; 7 | import net.porillo.effect.api.ScheduleClimateEffect; 8 | import net.porillo.engine.ClimateEngine; 9 | import net.porillo.engine.api.WorldClimateEngine; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.World; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.event.EventHandler; 14 | import org.bukkit.event.Listener; 15 | import org.bukkit.event.player.PlayerJoinEvent; 16 | import org.bukkit.potion.PotionEffect; 17 | import org.bukkit.potion.PotionEffectType; 18 | 19 | import java.util.UUID; 20 | 21 | @ClimateData(type = ClimateEffectType.PERMANENT_SLOWNESS) 22 | public class PermanentSlowness extends ScheduleClimateEffect implements Listener { 23 | 24 | private int duration; 25 | @Getter private double temperatureThreshold; 26 | 27 | private void updatePlayerSlowness(Player player, double temperature) { 28 | if (temperature >= temperatureThreshold) { 29 | PotionEffect potionEffect = new PotionEffect(PotionEffectType.SLOWNESS, duration, 1); 30 | player.addPotionEffect(potionEffect); 31 | } 32 | } 33 | 34 | @EventHandler 35 | public void onPlayerJoin(PlayerJoinEvent event) { 36 | UUID worldId = event.getPlayer().getWorld().getUID(); 37 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(worldId); 38 | if (climateEngine != null && climateEngine.isEffectEnabled(ClimateEffectType.PERMANENT_SLOWNESS)) { 39 | updatePlayerSlowness(event.getPlayer(), climateEngine.getTemperature()); 40 | } 41 | } 42 | 43 | @Override 44 | public void run() { 45 | for (World world : Bukkit.getWorlds()) { 46 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(world.getUID()); 47 | if (climateEngine != null && climateEngine.isEffectEnabled(ClimateEffectType.PERMANENT_SLOWNESS)) { 48 | for (Player player : world.getPlayers()) { 49 | updatePlayerSlowness(player, climateEngine.getTemperature()); 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Override 56 | public void setJsonModel(JsonObject jsonModel) { 57 | super.setJsonModel(jsonModel); 58 | setPeriod(jsonModel.get("interval").getAsInt()); 59 | duration = jsonModel.get("duration").getAsInt(); 60 | temperatureThreshold = jsonModel.get("threshold").getAsDouble(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/TrackedBlockTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.Getter; 4 | import net.porillo.objects.TrackedBlock; 5 | import org.bukkit.Location; 6 | 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.Map; 10 | 11 | @Getter 12 | public class TrackedBlockTable extends Table { 13 | /** 14 | * [TREE_ID | FURNACE_ID] -> [TREE | FURNACE] 15 | */ 16 | private Map blockMap = new HashMap<>(); 17 | 18 | /** 19 | * LOCATION -> [TREE_ID | FURNACE_ID] 20 | */ 21 | private Map locationMap = new HashMap<>(); 22 | 23 | /** 24 | * PLAYER_ID -> SET(TREE_ID | FURNACE_ID) 25 | */ 26 | private Map> playerMap = new HashMap<>(); 27 | 28 | TrackedBlockTable(String tableName) { 29 | super(tableName); 30 | } 31 | 32 | /** 33 | * Handles all storage for tree / furnace collections 34 | * 35 | * @param block tree / furnace 36 | */ 37 | public void updateCollections(TrackedBlock block) { 38 | //PLAYER_ID -> SET(TREE_ID | FURNACE_ID): 39 | final int ownerId = block.getOwnerId(); 40 | if (playerMap.containsKey(ownerId)) { 41 | HashSet idSet = playerMap.get(ownerId); 42 | idSet.add(block.getUniqueId()); 43 | } else { 44 | HashSet idSet = new HashSet<>(); 45 | idSet.add(block.getUniqueId()); 46 | playerMap.put(ownerId, idSet); 47 | } 48 | 49 | //[TREE_ID | FURNACE_ID] -> [TREE | FURNACE]: 50 | blockMap.put(block.getUniqueId(), block); 51 | 52 | //LOCATION -> [TREE_ID | FURNACE_ID]: 53 | locationMap.put(block.getLocation(), block.getUniqueId()); 54 | } 55 | 56 | /** 57 | * Determine if there is a tracked-block at the given location 58 | * - If so, update all block collections 59 | */ 60 | public TrackedBlock deleteLocation(Location location) { 61 | TrackedBlock deletedBlock = null; 62 | if (locationMap.containsKey(location)) { 63 | //LOCATION -> [TREE_ID | FURNACE_ID]: 64 | Integer blockId = locationMap.get(location); 65 | locationMap.remove(location); 66 | 67 | //[TREE_ID | FURNACE_ID] -> [TREE | FURNACE]: 68 | deletedBlock = blockMap.get(blockId); 69 | blockMap.remove(blockId); 70 | 71 | //PLAYER_ID -> SET(TREE_ID | FURNACE_ID): 72 | if (deletedBlock != null && playerMap.containsKey(deletedBlock.getOwnerId())) { 73 | playerMap.get(deletedBlock.getOwnerId()).remove(blockId); 74 | } 75 | } 76 | 77 | return deletedBlock; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/forest_bump.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "Forest Bump" 5 | }, 6 | "description": { 7 | "text": "Plant 64 saplings. Trivia: reforestation offsets climate change by sequestering carbon dioxide emissions from the atmosphere in trees and soil [IPCC, 2014]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:birch_leaves" 11 | }, 12 | "frame": "challenge", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "requirements": [ 18 | [ 19 | "forest_bump_oak", 20 | "forest_bump_birch", 21 | "forest_bump_spruce", 22 | "forest_bump_jungle", 23 | "forest_bump_acacia", 24 | "forest_bump_dark_oak" 25 | ] 26 | ], 27 | "rewards": { 28 | "experience": 10 29 | }, 30 | "criteria": { 31 | "forest_bump_oak": { 32 | "trigger": "minecraft:placed_block", 33 | "conditions": { 34 | "block": "minecraft:oak_sapling", 35 | "item": { 36 | "count": { 37 | "min": 64, 38 | "max": 64 39 | } 40 | } 41 | } 42 | }, 43 | "forest_bump_birch": { 44 | "trigger": "minecraft:placed_block", 45 | "conditions": { 46 | "block": "minecraft:birch_sapling", 47 | "item": { 48 | "count": { 49 | "min": 64, 50 | "max": 64 51 | } 52 | } 53 | } 54 | }, 55 | "forest_bump_spruce": { 56 | "trigger": "minecraft:placed_block", 57 | "conditions": { 58 | "block": "minecraft:spruce_sapling", 59 | "item": { 60 | "count": { 61 | "min": 64, 62 | "max": 64 63 | } 64 | } 65 | } 66 | }, 67 | "forest_bump_jungle": { 68 | "trigger": "minecraft:placed_block", 69 | "conditions": { 70 | "block": "minecraft:jungle_sapling", 71 | "item": { 72 | "count": { 73 | "min": 64, 74 | "max": 64 75 | } 76 | } 77 | } 78 | }, 79 | "forest_bump_acacia": { 80 | "trigger": "minecraft:placed_block", 81 | "conditions": { 82 | "block": "minecraft:acacia_sapling", 83 | "item": { 84 | "count": { 85 | "min": 64, 86 | "max": 64 87 | } 88 | } 89 | } 90 | }, 91 | "forest_bump_dark_oak": { 92 | "trigger": "minecraft:placed_block", 93 | "conditions": { 94 | "block": "minecraft:dark_oak_sapling", 95 | "item": { 96 | "count": { 97 | "min": 64, 98 | "max": 64 99 | } 100 | } 101 | } 102 | } 103 | }, 104 | "parent": "gw:green_thumbs_up" 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/util/Colorizer.java: -------------------------------------------------------------------------------- 1 | package net.porillo.util; 2 | 3 | import org.bukkit.ChatColor; 4 | 5 | import java.text.DecimalFormat; 6 | 7 | import static org.bukkit.ChatColor.*; 8 | 9 | public class Colorizer { 10 | 11 | private static final DecimalFormat decimalFormat = new DecimalFormat("#.##"); 12 | 13 | public static String formatIndex(double index, int score) { 14 | return String.format("%s%s", 15 | Colorizer.getScoreColor(score), 16 | decimalFormat.format(index)); 17 | } 18 | 19 | public static String formatScore(int score) { 20 | return String.format("%s%d", Colorizer.getScoreColor(score), score); 21 | } 22 | 23 | public static String formatTemp(double temp) { 24 | return String.format("%s%s", getTemperatureColor(temp), decimalFormat.format(temp)); 25 | } 26 | 27 | /** 28 | * Get the color associated with a carbon score 29 | * - Values are mapped to color-heat from LOW CO2 (cold) to HIGH CO2 (hot) 30 | * - These ranges are somewhat arbitrary 31 | */ 32 | public static ChatColor getScoreColor(int score) { 33 | ChatColor color; 34 | if (score <= -3500) { 35 | color = DARK_BLUE; 36 | } else if (score <= -2500) { 37 | color = BLUE; 38 | } else if (score <= -1500) { 39 | color = DARK_AQUA; 40 | } else if (score <= -500) { 41 | color = AQUA; 42 | } else if (score <= 500) { 43 | color = GREEN; // (-500, 500] 44 | } else if (score <= 1500) { 45 | color = YELLOW; 46 | } else if (score <= 2500) { 47 | color = GOLD; 48 | } else if (score <= 3500) { 49 | color = RED; 50 | } else { 51 | color = DARK_RED; 52 | } 53 | 54 | return color; 55 | } 56 | 57 | /** 58 | * Get the color associated with a temperature 59 | * - These ranges are somewhat arbitrary 60 | */ 61 | public static ChatColor getTemperatureColor(double temperature) { 62 | ChatColor color; 63 | if (temperature <= 10.5) { 64 | color = DARK_BLUE; 65 | } else if (temperature <= 11.5) { 66 | color = BLUE; 67 | } else if (temperature <= 12.5) { 68 | color = DARK_AQUA; 69 | } else if (temperature <= 13.5) { 70 | color = AQUA; 71 | } else if (temperature <= 14.5) { 72 | color = GREEN; // (13.5, 14.5] 73 | } else if (temperature <= 15.5) { 74 | color = YELLOW; 75 | } else if (temperature <= 16.5) { 76 | color = GOLD; 77 | } else if (temperature <= 17.5) { 78 | color = LIGHT_PURPLE; 79 | } else if (temperature <= 18.5) { 80 | color = RED; 81 | } else { 82 | color = DARK_RED; 83 | } 84 | 85 | return color; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/api/Model.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.api; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | 6 | import java.io.IOException; 7 | import java.net.URISyntaxException; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | 12 | public abstract class Model { 13 | 14 | @Getter private final String worldName; 15 | @Getter private final String modelName; 16 | private Path modelsPath; 17 | 18 | public Model(String worldName, String modelName) { 19 | this.modelName = modelName; 20 | this.worldName = worldName; 21 | 22 | if (GlobalWarming.getInstance() != null) { 23 | this.modelsPath = GlobalWarming.getInstance().getDataFolder().toPath().resolve("models"); 24 | } else { 25 | try { 26 | this.modelsPath = Paths.get(getClass().getResource("/models").toURI()); 27 | } catch (URISyntaxException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | } 32 | 33 | public Path getPath() { 34 | return this.modelsPath.resolve(worldName).resolve(modelName); 35 | } 36 | 37 | public String getContents() { 38 | createIfNotExists(); 39 | 40 | try { 41 | return new String(Files.readAllBytes(getPath())); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | 46 | return ""; 47 | } 48 | 49 | public void writeContents(String data) { 50 | clearFileForNewWrite(); 51 | 52 | try { 53 | Files.write(getPath(), data.getBytes()); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | 59 | private void createIfNotExists() { 60 | Path path = getPath(); 61 | if (!Files.exists(path)) { 62 | GlobalWarming.getInstance().getLogger().info(String.format( 63 | "Model: [%s] does not exist at: [%s], creating.", 64 | modelName, 65 | path)); 66 | 67 | try { 68 | // Copy resource from JAR to the correct path 69 | Files.createDirectories(modelsPath.resolve(worldName)); 70 | Files.copy(GlobalWarming.getInstance().getResource(String.format("models/%s", modelName)), path); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | } 76 | 77 | private void clearFileForNewWrite() { 78 | Path file = getPath(); 79 | try { 80 | if (Files.exists(file)) { 81 | Files.delete(file); 82 | Files.createFile(file); 83 | } else { 84 | Files.createFile(file); 85 | } 86 | } catch (IOException ex) { 87 | ex.printStackTrace(); 88 | } 89 | } 90 | 91 | public abstract void loadModel(); 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/Table.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import net.porillo.GlobalWarming; 6 | import net.porillo.database.queries.other.CreateTableQuery; 7 | import net.porillo.database.queue.AsyncDBQueue; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.net.URISyntaxException; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | 17 | @AllArgsConstructor 18 | public abstract class Table { 19 | 20 | @Getter private String tableName; 21 | 22 | public void createIfNotExists() { 23 | CreateTableQuery createTableQuery = new CreateTableQuery(getTableName(), loadSQLFromFile()); 24 | AsyncDBQueue.getInstance().queueCreateQuery(createTableQuery); 25 | } 26 | 27 | public Path getPath() { 28 | if (GlobalWarming.getInstance() != null) { 29 | Path path = GlobalWarming.getInstance().getDataFolder().toPath().resolve("scripts").resolve(String.format("%s.sql", tableName)); 30 | if (!Files.exists(path)) { 31 | GlobalWarming.getInstance().saveResource(String.format("scripts/%s.sql", tableName), false); 32 | } 33 | return GlobalWarming.getInstance().getDataFolder().toPath().resolve("scripts").resolve(String.format("%s.sql", tableName)); 34 | } else { 35 | try { 36 | return Paths.get(getClass().getResource("/scripts").toURI()).resolve(String.format("%s.sql", tableName)); 37 | } catch (URISyntaxException e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | 45 | private void copyFromResource() { 46 | Path path = getPath(); 47 | if (path == null || !Files.exists(path)) { 48 | GlobalWarming.getInstance().getLogger().info(String.format( 49 | "Script: [%s.sql] does not exist at: [%s], creating", 50 | tableName, 51 | path)); 52 | 53 | GlobalWarming.getInstance().saveResource(String.format("scripts/%s.sql", tableName), false); 54 | } 55 | } 56 | 57 | public String loadSQLFromFile() { 58 | this.copyFromResource(); 59 | StringBuilder builder = new StringBuilder(); 60 | Path path = getPath(); 61 | if (path != null) { 62 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(path)))) { 63 | String line; 64 | while ((line = reader.readLine()) != null) { 65 | if (!line.startsWith("--")) { 66 | builder.append(line); 67 | } 68 | } 69 | } catch (NullPointerException | IOException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | 74 | return builder.toString(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/database/TableUpdateTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import net.porillo.database.queries.insert.WorldInsertQuery; 4 | import net.porillo.database.queries.update.WorldUpdateQuery; 5 | import net.porillo.database.queue.AsyncDBQueue; 6 | import net.porillo.objects.GWorld; 7 | import org.testng.annotations.Test; 8 | 9 | import java.sql.Connection; 10 | import java.sql.PreparedStatement; 11 | import java.sql.ResultSet; 12 | import java.sql.SQLException; 13 | import java.util.Random; 14 | import java.util.UUID; 15 | 16 | import static org.hamcrest.MatcherAssert.assertThat; 17 | 18 | @Test 19 | public class TableUpdateTest { 20 | 21 | private Random random = new Random(); 22 | 23 | @Test 24 | public void testWorldUpdate() throws SQLException, ClassNotFoundException { 25 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 26 | AsyncDBQueue.getInstance().writeCreateTableQueue(connection); 27 | 28 | // Create a world and insert it into the DB 29 | final int uniqueId = random.nextInt(); 30 | UUID worldId = UUID.randomUUID(); 31 | GWorld gWorld = new GWorld(uniqueId, worldId, 0L, 0, 0, 0); 32 | AsyncDBQueue.getInstance().queueInsertQuery(new WorldInsertQuery(gWorld)); 33 | AsyncDBQueue.getInstance().writeInsertQueue(connection); 34 | 35 | // Verify the object exists in the DB 36 | String select = "SELECT * FROM worlds WHERE uniqueId = ?"; 37 | PreparedStatement insertStatement = connection.prepareStatement(select); 38 | insertStatement.setLong(1, uniqueId); 39 | ResultSet resultSet = insertStatement.executeQuery(); 40 | 41 | if (resultSet.last()) { 42 | assertThat("too many worlds", resultSet.getRow() == 1); 43 | } 44 | 45 | /* 46 | * GlobalWarming listeners will update the object in memory and 47 | * create a new update query object. Many updates to the same world or player 48 | * can occur nearly simultaneously. We want to test that only 1 update query 49 | * is actually executed against the Database. 50 | */ 51 | for (int i = 1; i <= 10; i++) { 52 | gWorld.setCarbonValue(i * 1000); 53 | AsyncDBQueue.getInstance().queueUpdateQuery(new WorldUpdateQuery(gWorld)); 54 | } 55 | 56 | GWorld updateQueryWorld = (GWorld) AsyncDBQueue.getInstance().getUpdateQueue().peek().getObject(); 57 | assertThat("world carbon score incorrect", updateQueryWorld.getCarbonValue().equals(10000)); 58 | assertThat("queue has duplicates", AsyncDBQueue.getInstance().getUpdateQueue().size() == 1); 59 | AsyncDBQueue.getInstance().writeUpdateQueue(connection); 60 | 61 | // Delete the test object from the database 62 | String delete = "DELETE FROM worlds WHERE uniqueId = ?"; 63 | PreparedStatement deleteStatement = connection.prepareStatement(delete); 64 | deleteStatement.setLong(1, uniqueId); 65 | deleteStatement.execute(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /gw_datapack/data/gw/advancements/if_a_tree_falls.json: -------------------------------------------------------------------------------- 1 | { 2 | "display": { 3 | "title": { 4 | "text": "If a Tree Falls..." 5 | }, 6 | "description": { 7 | "text": "Cut down a tree. Trivia: tropical deforestation is responsible for 3.0 billion tons of carbon dioxide a year [Harris, 2012]." 8 | }, 9 | "icon": { 10 | "item": "minecraft:birch_log" 11 | }, 12 | "frame": "task", 13 | "show_toast": true, 14 | "announce_to_chat": true, 15 | "hidden": false 16 | }, 17 | "requirements": [ 18 | [ 19 | "tree_falls_oak", 20 | "tree_falls_birch", 21 | "tree_falls_spruce", 22 | "tree_falls_jungle", 23 | "tree_falls_acacia", 24 | "tree_falls_dark_oak" 25 | ] 26 | ], 27 | "rewards": { 28 | "experience": 2 29 | }, 30 | "criteria": { 31 | "tree_falls_oak": { 32 | "trigger": "minecraft:inventory_changed", 33 | "conditions": { 34 | "items": [ 35 | { 36 | "item": "minecraft:oak_log", 37 | "count": { 38 | "min": 1, 39 | "max": 1 40 | } 41 | } 42 | ] 43 | } 44 | }, 45 | "tree_falls_birch": { 46 | "trigger": "minecraft:inventory_changed", 47 | "conditions": { 48 | "items": [ 49 | { 50 | "item": "minecraft:birch_log", 51 | "count": { 52 | "min": 1, 53 | "max": 1 54 | } 55 | } 56 | ] 57 | } 58 | }, 59 | "tree_falls_spruce": { 60 | "trigger": "minecraft:inventory_changed", 61 | "conditions": { 62 | "items": [ 63 | { 64 | "item": "minecraft:spruce_log", 65 | "count": { 66 | "min": 1, 67 | "max": 1 68 | } 69 | } 70 | ] 71 | } 72 | }, 73 | "tree_falls_jungle": { 74 | "trigger": "minecraft:inventory_changed", 75 | "conditions": { 76 | "items": [ 77 | { 78 | "item": "minecraft:jungle_log", 79 | "count": { 80 | "min": 1, 81 | "max": 1 82 | } 83 | } 84 | ] 85 | } 86 | }, 87 | "tree_falls_acacia": { 88 | "trigger": "minecraft:inventory_changed", 89 | "conditions": { 90 | "items": [ 91 | { 92 | "item": "minecraft:acacia_log", 93 | "count": { 94 | "min": 1, 95 | "max": 1 96 | } 97 | } 98 | ] 99 | } 100 | }, 101 | "tree_falls_dark_oak": { 102 | "trigger": "minecraft:inventory_changed", 103 | "conditions": { 104 | "items": [ 105 | { 106 | "item": "minecraft:dark_oak_log", 107 | "count": { 108 | "min": 1, 109 | "max": 1 110 | } 111 | } 112 | ] 113 | } 114 | } 115 | }, 116 | "parent": "gw:root" 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/neutral/MobSpawningRate.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.neutral; 2 | 3 | import net.porillo.GlobalWarming; 4 | import net.porillo.effect.ClimateData; 5 | import net.porillo.effect.api.ClimateEffectType; 6 | import net.porillo.effect.api.ListenerClimateEffect; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.MobDistribution; 9 | import net.porillo.engine.api.WorldClimateEngine; 10 | import org.bukkit.entity.*; 11 | import org.bukkit.event.EventHandler; 12 | import org.bukkit.event.entity.EntitySpawnEvent; 13 | 14 | @ClimateData(type = ClimateEffectType.MOB_SPAWN_RATE, provideModel = false) 15 | public class MobSpawningRate extends ListenerClimateEffect { 16 | 17 | @EventHandler 18 | public void onMobSpawn(EntitySpawnEvent event) { 19 | WorldClimateEngine worldEngine = ClimateEngine.getInstance().getClimateEngine(event.getLocation().getWorld().getUID()); 20 | if (worldEngine != null && worldEngine.isEffectEnabled(ClimateEffectType.MOB_SPAWN_RATE)) { 21 | MobDistribution distribution = worldEngine.getEntityFitnessModel().getEntityFitnessMap().get(event.getEntityType()); 22 | if (distribution != null) { 23 | double chance = distribution.getValue(worldEngine.getTemperature()); 24 | double random = GlobalWarming.getInstance().getRandom().nextDouble(); 25 | if (chance / 100.f <= random) { 26 | //Cancel the mob: 27 | event.setCancelled(true); 28 | 29 | //Spawn an alternative, if available: 30 | String alternative = distribution.getAlternate(); 31 | if (alternative != null && !alternative.isEmpty()) { 32 | try { 33 | //Spawn: 34 | Entity entity = event.getLocation().getWorld().spawn( 35 | event.getLocation(), 36 | EntityType.fromName(alternative).getEntityClass()); 37 | 38 | //Make it a baby, if possible (for fun): 39 | if (entity instanceof Ageable) { 40 | ((Ageable) entity).setBaby(); 41 | } else switch (entity.getType()) { 42 | case PHANTOM: 43 | ((Phantom) entity).setSize(1); 44 | break; 45 | case ZOMBIE: 46 | case DROWNED: 47 | case HUSK: 48 | case ZOMBIE_VILLAGER: 49 | ((Zombie) entity).setBaby(true); 50 | break; 51 | } 52 | 53 | } catch (Exception e) { 54 | GlobalWarming.getInstance().getLogger().warning(String.format( 55 | "Error spawning alternate mob: [%s]", 56 | distribution.getAlternate())); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/negative/Fire.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.negative; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.effect.api.ScheduleClimateEffect; 10 | import net.porillo.engine.ClimateEngine; 11 | import net.porillo.engine.api.FireDistribution; 12 | import net.porillo.engine.api.WorldClimateEngine; 13 | import org.bukkit.Bukkit; 14 | import org.bukkit.Chunk; 15 | import org.bukkit.Material; 16 | import org.bukkit.World; 17 | import org.bukkit.block.Block; 18 | import org.bukkit.block.BlockFace; 19 | import org.bukkit.event.Listener; 20 | 21 | @ClimateData(type = ClimateEffectType.FIRE) 22 | public class Fire extends ScheduleClimateEffect implements Listener { 23 | 24 | private static final int BLOCKS_PER_CHUNK = 16; 25 | @Getter private FireDistribution fireMap; 26 | 27 | /** 28 | * Set a random set of loaded blocks on fire 29 | */ 30 | private void setFire(World world, int blocks) { 31 | if (world != null) { 32 | int count = world.getLoadedChunks().length; 33 | for (int i = 0; i < blocks; i++) { 34 | int chunkIndex = GlobalWarming.getInstance().getRandom().nextInt(count); 35 | Chunk chunk = world.getLoadedChunks()[chunkIndex]; 36 | int x = (chunk.getX() * BLOCKS_PER_CHUNK) + GlobalWarming.getInstance().getRandom().nextInt(BLOCKS_PER_CHUNK); 37 | int z = (chunk.getZ() * BLOCKS_PER_CHUNK) + GlobalWarming.getInstance().getRandom().nextInt(BLOCKS_PER_CHUNK); 38 | Block topBlock = world.getHighestBlockAt(x, z); 39 | topBlock.getRelative(BlockFace.UP).setType(Material.FIRE); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Periodically check if a fire should be set 46 | */ 47 | @Override 48 | public void run() { 49 | for (World world : Bukkit.getWorlds()) { 50 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(world.getUID()); 51 | if (climateEngine != null && 52 | climateEngine.isEffectEnabled(ClimateEffectType.FIRE)) { 53 | double random = GlobalWarming.getInstance().getRandom().nextDouble(); 54 | double chance = fireMap.getValue(climateEngine.getTemperature()); 55 | if (random <= chance / 100.f) { 56 | int blocks = (int) fireMap.getBlocks(climateEngine.getTemperature()); 57 | setFire(world, blocks); 58 | } 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Load the fire distribution model 65 | */ 66 | @Override 67 | public void setJsonModel(JsonObject jsonModel) { 68 | super.setJsonModel(jsonModel); 69 | fireMap = GlobalWarming.getInstance().getGson().fromJson( 70 | jsonModel.get("distribution"), 71 | new TypeToken() { 72 | }.getType()); 73 | 74 | if (fireMap == null) { 75 | unregister(); 76 | } else { 77 | setPeriod(jsonModel.get("interval").getAsInt()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/WorldTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import net.porillo.GlobalWarming; 4 | import net.porillo.config.WorldConfig; 5 | import net.porillo.database.api.SelectCallback; 6 | import net.porillo.database.queries.insert.WorldInsertQuery; 7 | import net.porillo.database.queries.select.WorldSelectQuery; 8 | import net.porillo.database.queries.update.WorldUpdateQuery; 9 | import net.porillo.database.queue.AsyncDBQueue; 10 | import net.porillo.objects.GWorld; 11 | import org.bukkit.scheduler.BukkitRunnable; 12 | 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | 18 | public class WorldTable extends Table implements SelectCallback { 19 | 20 | private Map worldMap = new HashMap<>(); 21 | 22 | public WorldTable() { 23 | super("worlds"); 24 | createIfNotExists(); 25 | 26 | WorldSelectQuery selectQuery = new WorldSelectQuery(this); 27 | AsyncDBQueue.getInstance().queueSelectQuery(selectQuery); 28 | } 29 | 30 | public GWorld getWorld(UUID worldId) { 31 | if (worldMap.containsKey(worldId)) { 32 | return worldMap.get(worldId); 33 | } 34 | 35 | return insertNewWorld(worldId); 36 | } 37 | 38 | private void updateWorld(GWorld gWorld) { 39 | worldMap.put(gWorld.getWorldId(), gWorld); 40 | } 41 | 42 | public GWorld insertNewWorld(UUID worldId) { 43 | GWorld gWorld = new GWorld(); 44 | gWorld.setUniqueID(GlobalWarming.getInstance().getRandom().nextInt(Integer.MAX_VALUE)); 45 | gWorld.setWorldId(worldId); 46 | gWorld.setFirstSeen(System.currentTimeMillis()); 47 | gWorld.setCarbonValue(0); 48 | gWorld.setSeaLevel(0); 49 | gWorld.setSize(0); 50 | 51 | updateWorld(gWorld); 52 | 53 | WorldInsertQuery worldInsertQuery = new WorldInsertQuery(gWorld); 54 | AsyncDBQueue.getInstance().queueInsertQuery(worldInsertQuery); 55 | GlobalWarming.getInstance().getLogger().info(String.format( 56 | "Record created for world: [%s]", 57 | WorldConfig.getDisplayName(worldId))); 58 | return gWorld; 59 | } 60 | 61 | public void updateWorldCarbonValue(UUID worldId, int value) { 62 | GWorld affectedWorld = this.getWorld(worldId); 63 | 64 | if (affectedWorld != null) { 65 | int carbon = affectedWorld.getCarbonValue(); 66 | affectedWorld.setCarbonValue(carbon + value); 67 | 68 | //Queue an update to the world table: 69 | WorldUpdateQuery worldUpdateQuery = new WorldUpdateQuery(affectedWorld); 70 | AsyncDBQueue.getInstance().queueUpdateQuery(worldUpdateQuery); 71 | } 72 | } 73 | 74 | @Override 75 | public void onSelectionCompletion(List returnList) { 76 | if (GlobalWarming.getInstance() != null) { 77 | new BukkitRunnable() { 78 | @Override 79 | public void run() { 80 | for (GWorld world : returnList) { 81 | updateWorld(world); 82 | } 83 | } 84 | }.runTask(GlobalWarming.getInstance()); 85 | } else { 86 | System.out.printf("Selection returned %d worlds.%n", returnList.size()); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/models/ScoreTempModel.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine.models; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | import lombok.Getter; 7 | import net.porillo.engine.api.Distribution; 8 | import net.porillo.engine.api.Model; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.Comparator; 12 | import java.util.Map; 13 | import java.util.TreeMap; 14 | 15 | public class ScoreTempModel extends Model { 16 | 17 | /** 18 | * Carbon sensitivity 19 | * - LOW: [-1M, 1M] (e.g., many players) 20 | * - HIGH: [-50K, 50K] (e.g., fewer players) 21 | * - VERY_HIGH: [-800, 1200] (e.g., demo) 22 | */ 23 | public enum CarbonSensitivity { 24 | LOW, 25 | HIGH, 26 | VERY_HIGH 27 | } 28 | 29 | @Getter private Map indexMap; 30 | private CarbonSensitivity sensitivity; 31 | private Distribution distribution; 32 | Map> temperatureMap; 33 | 34 | public ScoreTempModel(String worldName, CarbonSensitivity sensitivity) { 35 | super(worldName, "scoreTempModel.json"); 36 | this.sensitivity = sensitivity; 37 | this.loadModel(); 38 | } 39 | 40 | public Map getTemperatureMap() { 41 | return temperatureMap.get(sensitivity); 42 | } 43 | 44 | /** 45 | * Load the score / temperature model 46 | * - CarbonSensitivity specifies the range of scores being mapped onto [10C, 20C] 47 | */ 48 | @Override 49 | public void loadModel() { 50 | //Deserialize the model from JSON: 51 | GsonBuilder builder = new GsonBuilder(); 52 | Gson gson = builder.enableComplexMapKeySerialization().create(); 53 | Type listType = new TypeToken>>() { 54 | }.getType(); 55 | temperatureMap = gson.fromJson(super.getContents(), listType); 56 | 57 | //Validate the result: 58 | this.indexMap = new TreeMap<>(Comparator.naturalOrder()); 59 | if (temperatureMap == null || !temperatureMap.containsKey(sensitivity)) { 60 | throw new RuntimeException(String.format("Invalid score-temperature model (%s): [%s]", sensitivity, super.getPath())); 61 | } 62 | 63 | if (temperatureMap.get(sensitivity).isEmpty()) { 64 | throw new RuntimeException(String.format("No values found in (%s): [%s]", sensitivity, super.getPath())); 65 | } 66 | 67 | //Copy the result: 68 | this.indexMap.putAll(temperatureMap.get(sensitivity)); 69 | 70 | //Create a lookup function: 71 | int i = 0; 72 | double[] scores = new double[indexMap.size()]; 73 | double[] temps = new double[indexMap.size()]; 74 | for (Map.Entry entry : indexMap.entrySet()) { 75 | scores[i] = (double) entry.getKey(); 76 | temps[i++] = entry.getValue(); 77 | } 78 | this.distribution = new Distribution(scores, temps); 79 | } 80 | 81 | /** 82 | * Get the temperature using linear interpolation 83 | * - Limits score to the domain to prevent exceptions 84 | * - This value is used by most models 85 | */ 86 | public double getTemperature(int score) { 87 | return distribution.getValue((double) score); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/engine/ClimateEngine.java: -------------------------------------------------------------------------------- 1 | package net.porillo.engine; 2 | 3 | import net.porillo.GlobalWarming; 4 | import net.porillo.config.Lang; 5 | import net.porillo.config.WorldConfig; 6 | import net.porillo.database.tables.WorldTable; 7 | import net.porillo.engine.api.WorldClimateEngine; 8 | import net.porillo.objects.GWorld; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.World; 11 | import org.bukkit.scheduler.BukkitRunnable; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.UUID; 16 | 17 | public class ClimateEngine { 18 | 19 | private static ClimateEngine climateEngine; 20 | private Map worldClimateEngines; 21 | 22 | public ClimateEngine() { 23 | this.worldClimateEngines = new HashMap<>(); 24 | } 25 | 26 | public void loadWorldClimateEngine(World world) { 27 | if (world == null) return; 28 | UUID worldId = world.getUID(); 29 | if (!worldClimateEngines.containsKey(worldId)) { 30 | WorldConfig worldConfig = new WorldConfig(worldId); 31 | 32 | if (!worldConfig.isEnabled()) { 33 | GlobalWarming.getInstance().getLogger().info(String.format("World: [%s] found, but is disabled", world.getName())); 34 | } else { 35 | GlobalWarming.getInstance().getLogger().info(String.format("Loading climate engine for: [%s]", world.getName())); 36 | } 37 | 38 | //Add the climate engine: 39 | worldClimateEngines.put(worldId, new WorldClimateEngine(worldConfig)); 40 | 41 | //Delayed attempt create the world object if it doesn't currently exist: 42 | WorldTable worldTable = GlobalWarming.getInstance().getTableManager().getWorldTable(); 43 | GWorld gWorld = worldTable.getWorld(worldId); 44 | if (gWorld == null) { 45 | new BukkitRunnable() { 46 | @Override 47 | public void run() { 48 | GWorld gw = worldTable.getWorld(worldId); 49 | if (gw == null) { 50 | worldTable.insertNewWorld(worldId); 51 | } 52 | } 53 | }.runTaskLater(GlobalWarming.getInstance(), 40L); 54 | } 55 | } 56 | } 57 | 58 | public void loadWorldClimateEngines() { 59 | for (World world : Bukkit.getWorlds()) { 60 | loadWorldClimateEngine(world); 61 | } 62 | } 63 | 64 | public WorldClimateEngine getClimateEngine(UUID worldId) { 65 | WorldClimateEngine climateEngine = worldClimateEngines.get(worldId); 66 | 67 | if (climateEngine == null) { 68 | GlobalWarming.getInstance().getLogger().warning(String.format( 69 | Lang.ENGINE_NOTFOUND.get(), 70 | WorldConfig.getDisplayName(worldId))); 71 | } 72 | 73 | return climateEngine; 74 | } 75 | 76 | public boolean isClimateEngineEnabled(UUID worldId) { 77 | WorldClimateEngine worldClimateEngine = getClimateEngine(worldId); 78 | return worldClimateEngine != null && worldClimateEngine.isEnabled(); 79 | } 80 | 81 | public static ClimateEngine getInstance() { 82 | if (climateEngine == null) { 83 | climateEngine = new ClimateEngine(); 84 | } 85 | 86 | return climateEngine; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/config/ConfigLoader.java: -------------------------------------------------------------------------------- 1 | package net.porillo.config; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | import org.bukkit.configuration.file.FileConfiguration; 6 | import org.bukkit.configuration.file.YamlConfiguration; 7 | 8 | import java.io.*; 9 | 10 | public abstract class ConfigLoader { 11 | 12 | FileConfiguration conf; 13 | @Getter private String fileName; 14 | private File configFile; 15 | 16 | ConfigLoader(String fileName, String resource) { 17 | this.fileName = fileName; 18 | File dataFolder = GlobalWarming.getInstance().getDataFolder(); 19 | configFile = new File(dataFolder, File.separator + fileName); 20 | if (!configFile.exists()) { 21 | if (!dataFolder.exists()) { 22 | dataFolder.mkdir(); 23 | } 24 | 25 | try { 26 | configFile.createNewFile(); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | writeConfig(GlobalWarming.getInstance().getResource(resource)); 32 | } 33 | 34 | conf = YamlConfiguration.loadConfiguration(configFile); 35 | } 36 | 37 | ConfigLoader(String fileName) { 38 | this(fileName, fileName); 39 | } 40 | 41 | private void addDefaults() { 42 | conf.options().copyDefaults(true); 43 | saveConfig(); 44 | } 45 | 46 | void load() { 47 | if (!configFile.exists()) { 48 | GlobalWarming.getInstance().getDataFolder().mkdir(); 49 | saveConfig(); 50 | } 51 | addDefaults(); 52 | loadKeys(); 53 | } 54 | 55 | protected abstract void loadKeys(); 56 | 57 | protected void reload() { 58 | rereadFromDisk(); 59 | load(); 60 | } 61 | 62 | private void rereadFromDisk() { 63 | conf = YamlConfiguration.loadConfiguration(configFile); 64 | } 65 | 66 | private void saveConfig() { 67 | try { 68 | conf.save(configFile); 69 | } catch (IOException ex) { 70 | ex.printStackTrace(); 71 | } 72 | } 73 | 74 | void saveIfNotExist() { 75 | if (!configFile.exists()) { 76 | if (GlobalWarming.getInstance().getResource(fileName) != null) { 77 | GlobalWarming.getInstance().getLogger().info(String.format("Saving [%s] to disk", fileName)); 78 | GlobalWarming.getInstance().saveResource(fileName, false); 79 | } 80 | } 81 | 82 | rereadFromDisk(); 83 | } 84 | 85 | void set(String key, Object value) { 86 | conf.set(key, value); 87 | } 88 | 89 | private void writeConfig(InputStream in) { 90 | OutputStream out = null; 91 | try { 92 | out = new FileOutputStream(configFile); 93 | int read; 94 | byte[] bytes = new byte[1024]; 95 | while ((read = in.read(bytes)) != -1) { 96 | out.write(bytes, 0, read); 97 | } 98 | 99 | out.flush(); 100 | } catch (Exception e) { 101 | e.printStackTrace(); 102 | } finally { 103 | try { 104 | if (in != null) { 105 | in.close(); 106 | } 107 | 108 | if (out != null) { 109 | out.close(); 110 | } 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/objects/GPlayer.java: -------------------------------------------------------------------------------- 1 | package net.porillo.objects; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import net.porillo.config.Lang; 7 | import net.porillo.engine.ClimateEngine; 8 | import net.porillo.engine.api.WorldClimateEngine; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.OfflinePlayer; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.sql.ResultSet; 14 | import java.sql.SQLException; 15 | import java.util.UUID; 16 | 17 | @Data 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class GPlayer { 21 | 22 | /** 23 | * Unique ID in database 24 | */ 25 | private Integer uniqueId; 26 | 27 | /** 28 | * All players have a UUID assigned when they first join by CraftBukkit 29 | */ 30 | private UUID uuid; 31 | 32 | /** 33 | * Log the first time this plugin has seen the player 34 | * CraftBukkit tracks the players first seen on the server, but 35 | * this plugin might be installed after that 36 | */ 37 | private long firstSeen; 38 | 39 | /** 40 | * Numerical "carbon score" value for just this player 41 | */ 42 | private Integer carbonScore; 43 | 44 | /** 45 | * Track each player's current world 46 | * - This information is needed when players go offline, but have planted saplings, 47 | * left furnaces running or have active bounties 48 | */ 49 | private UUID worldId; 50 | 51 | public GPlayer(ResultSet rs) throws SQLException { 52 | this.uniqueId = rs.getInt(1); 53 | this.uuid = UUID.fromString(rs.getString(2)); 54 | this.firstSeen = rs.getLong(3); 55 | this.carbonScore = rs.getInt(4); 56 | this.worldId = UUID.fromString(rs.getString(5)); 57 | } 58 | 59 | /** 60 | * @return player-record if online, NULL otherwise 61 | */ 62 | public Player getOnlinePlayer() { 63 | return Bukkit.getPlayer(uuid); 64 | } 65 | 66 | /** 67 | * @return never NULL, even when player-record does not exist 68 | */ 69 | public OfflinePlayer getOfflinePlayer() { 70 | return Bukkit.getOfflinePlayer(uuid); 71 | } 72 | 73 | public UUID getAssociatedWorldId() { 74 | UUID associatedWorldId = null; 75 | WorldClimateEngine climateEngine = ClimateEngine.getInstance().getClimateEngine(getWorldId()); 76 | if (climateEngine != null) { 77 | associatedWorldId = climateEngine.getConfig().getAssociatedWorldId(); 78 | } 79 | 80 | return associatedWorldId; 81 | } 82 | 83 | public void sendMsg(String msg) { 84 | Player onlinePlayer = getOnlinePlayer(); 85 | if (onlinePlayer != null) { 86 | onlinePlayer.sendMessage(msg); 87 | } 88 | } 89 | 90 | public void sendMsg(Lang lang) { 91 | sendMsg(lang.get()); 92 | } 93 | 94 | public void sendMsg(Lang lang, Object... args) { 95 | sendMsg(lang.get(args)); 96 | } 97 | 98 | @Override 99 | public boolean equals(Object o) { 100 | if (this == o) return true; 101 | if (o == null || getClass() != o.getClass()) return false; 102 | if (!super.equals(o)) return false; 103 | 104 | GPlayer gPlayer = (GPlayer) o; 105 | return uniqueId.equals(gPlayer.uniqueId); 106 | } 107 | 108 | @Override 109 | public int hashCode() { 110 | int result = super.hashCode(); 111 | result = 31 * result + uniqueId.hashCode(); 112 | return result; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/listeners/PlayerListener.java: -------------------------------------------------------------------------------- 1 | package net.porillo.listeners; 2 | 3 | import lombok.AllArgsConstructor; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.commands.GeneralCommands; 6 | import net.porillo.database.queries.update.PlayerUpdateQuery; 7 | import net.porillo.database.queue.AsyncDBQueue; 8 | import net.porillo.database.tables.PlayerTable; 9 | import net.porillo.engine.ClimateEngine; 10 | import net.porillo.engine.api.WorldClimateEngine; 11 | import net.porillo.objects.GPlayer; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.event.EventHandler; 14 | import org.bukkit.event.Listener; 15 | import org.bukkit.event.player.PlayerChangedWorldEvent; 16 | import org.bukkit.event.player.PlayerJoinEvent; 17 | import org.bukkit.event.player.PlayerQuitEvent; 18 | 19 | @AllArgsConstructor 20 | public class PlayerListener implements Listener { 21 | 22 | private GlobalWarming gw; 23 | 24 | @EventHandler(ignoreCancelled = true) 25 | public void onPlayerJoin(PlayerJoinEvent event) { 26 | //Player lookup: 27 | PlayerTable table = gw.getTableManager().getPlayerTable(); 28 | Player player = event.getPlayer(); 29 | GPlayer gPlayer = table.getOrCreatePlayer(player.getUniqueId()); 30 | 31 | //First-time players will receive an instructional booklet: 32 | // - Note: adding it even if the climate-engine is disabled (in case it is enabled later) 33 | if (!player.hasPlayedBefore()) { 34 | GeneralCommands.getBooklet(gPlayer); 35 | } 36 | 37 | //Add the scoreboard if the climate engine for the player's associated-world is enabled 38 | // - Note: scores are not tied to the player's current-world 39 | WorldClimateEngine engine = 40 | ClimateEngine.getInstance().getClimateEngine(gPlayer.getAssociatedWorldId()); 41 | 42 | if (engine != null && engine.isEnabled()) { 43 | if (gw.getConf().isWelcomingOnJoin()) { 44 | //Show players their current carbon score in chat: 45 | GeneralCommands.showCarbonScore(gPlayer); 46 | } 47 | 48 | if (gw.getConf().isScoreboardEnabled()) { 49 | //Add the player to the scoreboard: 50 | gw.getScoreboard().connect(gPlayer); 51 | } 52 | } 53 | } 54 | 55 | /** 56 | * When players leave: 57 | * - Remove them from the scoreboard 58 | */ 59 | @EventHandler(ignoreCancelled = true) 60 | public void onPlayerQuit(PlayerQuitEvent event) { 61 | if (gw.getConf().isScoreboardEnabled()) { 62 | PlayerTable playerTable = gw.getTableManager().getPlayerTable(); 63 | GPlayer gPlayer = playerTable.getPlayers().get(event.getPlayer().getUniqueId()); 64 | gw.getScoreboard().disconnect(gPlayer); 65 | } 66 | } 67 | 68 | /** 69 | * Track a player's current world 70 | * - This information is needed when players go offline, but have planted saplings, 71 | * left furnaces running or have active bounties 72 | */ 73 | @EventHandler(ignoreCancelled = true) 74 | public void onPlayerChangedWorldEvent(PlayerChangedWorldEvent event) { 75 | //Update the player's world: 76 | PlayerTable playerTable = gw.getTableManager().getPlayerTable(); 77 | GPlayer gPlayer = playerTable.getPlayers().get(event.getPlayer().getUniqueId()); 78 | gPlayer.setWorldId(event.getPlayer().getWorld().getUID()); 79 | 80 | //Database update: 81 | PlayerUpdateQuery updateQuery = new PlayerUpdateQuery(gPlayer); 82 | AsyncDBQueue.getInstance().queueUpdateQuery(updateQuery); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/EntityTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.database.api.SelectCallback; 6 | import net.porillo.database.queries.delete.EntityDeleteQuery; 7 | import net.porillo.database.queries.select.EntitySelectQuery; 8 | import net.porillo.database.queries.update.EntityUpdateQuery; 9 | import net.porillo.database.queue.AsyncDBQueue; 10 | import net.porillo.objects.TrackedEntity; 11 | import org.bukkit.scheduler.BukkitRunnable; 12 | 13 | import java.util.*; 14 | 15 | @Getter 16 | public class EntityTable extends Table implements SelectCallback { 17 | 18 | /** 19 | * [ENTITY_ID] -> [ENTITY] 20 | */ 21 | private Map entityMap = new HashMap<>(); 22 | 23 | /** 24 | * PLAYER_ID -> SET(ENTITY_ID) 25 | */ 26 | private Map> playerMap = new HashMap<>(); 27 | 28 | public EntityTable() { 29 | super("entities"); 30 | createIfNotExists(); 31 | 32 | EntitySelectQuery selectQuery = new EntitySelectQuery(this); 33 | AsyncDBQueue.getInstance().queueSelectQuery(selectQuery); 34 | 35 | } 36 | 37 | @Override 38 | public void onSelectionCompletion(List returnList) { 39 | if (GlobalWarming.getInstance() != null) { 40 | new BukkitRunnable() { 41 | @Override 42 | public void run() { 43 | for (TrackedEntity entity : returnList) { 44 | updateCollections(entity); 45 | } 46 | } 47 | }.runTask(GlobalWarming.getInstance()); 48 | } else { 49 | System.out.printf("Selection returned %d furnaces.%n", returnList.size()); 50 | } 51 | } 52 | 53 | public TrackedEntity delete(UUID entityId, boolean purge) { 54 | TrackedEntity trackedEntity = this.deleteEntity(entityId); 55 | if (trackedEntity != null) { 56 | if (purge) { 57 | EntityDeleteQuery deleteQuery = new EntityDeleteQuery(trackedEntity); 58 | AsyncDBQueue.getInstance().queueDeleteQuery(deleteQuery); 59 | } else { 60 | EntityUpdateQuery updateQuery = new EntityUpdateQuery(trackedEntity); 61 | AsyncDBQueue.getInstance().queueUpdateQuery(updateQuery); 62 | } 63 | } 64 | 65 | return trackedEntity; 66 | } 67 | 68 | 69 | private TrackedEntity deleteEntity(UUID entityId) { 70 | TrackedEntity deletedEntity = null; 71 | 72 | if (entityMap.containsKey(entityId)) { 73 | deletedEntity = entityMap.remove(entityId); 74 | 75 | //PLAYER_ID -> SET(ENTITY_ID): 76 | if (deletedEntity != null && playerMap.containsKey(deletedEntity.getBreederId())) { 77 | playerMap.get(deletedEntity.getBreederId()).remove(entityId); 78 | } 79 | } 80 | 81 | return deletedEntity; 82 | } 83 | 84 | /** 85 | * Handles all storage for entities collections 86 | * 87 | * @param entity Tracked Entity 88 | */ 89 | public void updateCollections(TrackedEntity entity) { 90 | //PLAYER_ID -> SET(ENTITY_ID): 91 | final int breederID = entity.getBreederId(); 92 | 93 | if (!playerMap.containsKey(breederID)) { 94 | HashSet idSet = new HashSet<>(); 95 | idSet.add(entity.getUuid()); 96 | playerMap.put(breederID, idSet); 97 | } else { 98 | HashSet idSet = playerMap.get(breederID); 99 | idSet.add(entity.getUuid()); 100 | } 101 | 102 | //[ENTITY_ID] -> [ENTITY]: 103 | entityMap.put(entity.getUuid(), entity); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/net/porillo/database/TableInsertTest.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database; 2 | 3 | import net.porillo.database.queries.insert.ContributionInsertQuery; 4 | import net.porillo.database.queries.insert.PlayerInsertQuery; 5 | import net.porillo.database.queries.insert.WorldInsertQuery; 6 | import net.porillo.database.queue.AsyncDBQueue; 7 | import net.porillo.objects.Contribution; 8 | import net.porillo.objects.GPlayer; 9 | import net.porillo.objects.GWorld; 10 | import org.testng.annotations.Test; 11 | 12 | import java.sql.Connection; 13 | import java.sql.PreparedStatement; 14 | import java.sql.ResultSet; 15 | import java.sql.SQLException; 16 | import java.util.Random; 17 | import java.util.UUID; 18 | 19 | import static org.hamcrest.MatcherAssert.assertThat; 20 | 21 | @Test 22 | public class TableInsertTest { 23 | 24 | @Test 25 | public void testWorldInserts() throws SQLException, ClassNotFoundException { 26 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 27 | 28 | for (int i = 0; i < 30; i++) { 29 | GWorld gWorld = TestUtility.getInstance().nextRandomWorld(); 30 | AsyncDBQueue.getInstance().queueInsertQuery(new WorldInsertQuery(gWorld)); 31 | } 32 | 33 | AsyncDBQueue.getInstance().writeInsertQueue(connection); 34 | } 35 | 36 | @Test 37 | public void testPlayerInserts() throws SQLException, ClassNotFoundException { 38 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 39 | 40 | for (int i = 0; i < 300; i++) { 41 | GPlayer gPlayer = TestUtility.getInstance().nextRandomPlayer(); 42 | AsyncDBQueue.getInstance().queueInsertQuery(new PlayerInsertQuery(gPlayer)); 43 | } 44 | 45 | AsyncDBQueue.getInstance().writeInsertQueue(connection); 46 | } 47 | 48 | @Test 49 | public void testContributionTable() throws SQLException, ClassNotFoundException { 50 | Connection connection = TestUtility.getInstance().getConnectionManager().openConnection(); 51 | 52 | Random random = TestUtility.getInstance().getRandom(); 53 | // Create a contribution and insert it into the DB 54 | final Integer uniqueId = random.nextInt(Integer.MAX_VALUE); 55 | UUID worldId = UUID.randomUUID(); 56 | Contribution contribution = 57 | new Contribution(uniqueId, random.nextInt(Integer.MAX_VALUE), random.nextInt(Integer.MAX_VALUE), worldId, 15); 58 | AsyncDBQueue.getInstance().queueInsertQuery(new ContributionInsertQuery(contribution)); 59 | AsyncDBQueue.getInstance().writeInsertQueue(connection); 60 | 61 | // Verify the object exists in the DB 62 | String select = "SELECT * FROM contributions WHERE uniqueId = ?"; 63 | PreparedStatement insertStatement = connection.prepareStatement(select); 64 | insertStatement.setLong(1, uniqueId); 65 | ResultSet resultSet = insertStatement.executeQuery(); 66 | 67 | // Validate the object is correct 68 | while (resultSet.next()) { 69 | assertThat("pk mismatch", 70 | uniqueId.equals(resultSet.getInt(1))); 71 | assertThat("contributor mismatch", 72 | contribution.getContributer().equals(resultSet.getInt(2))); 73 | assertThat("contribKey mismatch", 74 | contribution.getContributionKey().equals(resultSet.getInt(3))); 75 | assertThat("worldId mismatch", 76 | contribution.getWorldId().equals(UUID.fromString(resultSet.getString(4)))); 77 | assertThat("value mismatch", 78 | contribution.getContributionValue() == resultSet.getInt(5)); 79 | } 80 | } 81 | 82 | // TODO: Add tests for all other tables 83 | // Warning, some object constructors require bukkit api methods 84 | // which can be cumbersome to work with in a testing environment 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/database/tables/PlayerTable.java: -------------------------------------------------------------------------------- 1 | package net.porillo.database.tables; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.database.api.SelectCallback; 6 | import net.porillo.database.queries.insert.PlayerInsertQuery; 7 | import net.porillo.database.queries.select.PlayerSelectQuery; 8 | import net.porillo.database.queue.AsyncDBQueue; 9 | import net.porillo.objects.GPlayer; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.scheduler.BukkitRunnable; 13 | 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.UUID; 18 | 19 | @Getter 20 | public class PlayerTable extends Table implements SelectCallback { 21 | 22 | private Map players = new HashMap<>(); 23 | private Map uuidMap = new HashMap<>(); 24 | 25 | public PlayerTable() { 26 | super("players"); 27 | createIfNotExists(); 28 | 29 | PlayerSelectQuery selectQuery = new PlayerSelectQuery(this); 30 | AsyncDBQueue.getInstance().queueSelectQuery(selectQuery); 31 | } 32 | 33 | public GPlayer getOrCreatePlayer(UUID uuid) { 34 | GPlayer gPlayer; 35 | if (players.containsKey(uuid)) { 36 | //Existing players: 37 | gPlayer = players.get(uuid); 38 | } else { 39 | //New players: 40 | // - Get the player's world when known 41 | // - Note: the player record is NULL when using the untracked UUID 42 | UUID worldId = null; 43 | Player onlinePlayer = Bukkit.getPlayer(uuid); 44 | if (onlinePlayer != null) { 45 | worldId = onlinePlayer.getWorld().getUID(); 46 | } 47 | 48 | //Or use the default world: 49 | if (worldId == null) { 50 | worldId = Bukkit.getWorlds().get(0).getUID(); 51 | } 52 | 53 | //GW identifier: 54 | Integer uniqueId = 55 | onlinePlayer == null 56 | ? 0 57 | : GlobalWarming.getInstance().getRandom().nextInt(Integer.MAX_VALUE); 58 | 59 | //New player: 60 | gPlayer = new GPlayer( 61 | uniqueId, 62 | uuid, 63 | System.currentTimeMillis(), 64 | 0, 65 | worldId); 66 | 67 | //Local storage: 68 | players.put(uuid, gPlayer); 69 | uuidMap.put(uniqueId, uuid); 70 | 71 | //Database update: 72 | PlayerInsertQuery insertQuery = new PlayerInsertQuery(gPlayer); 73 | AsyncDBQueue.getInstance().queueInsertQuery(insertQuery); 74 | } 75 | 76 | return gPlayer; 77 | } 78 | 79 | @Override 80 | public void onSelectionCompletion(List returnList) { 81 | if (GlobalWarming.getInstance() != null) { 82 | new BukkitRunnable() { 83 | @Override 84 | public void run() { 85 | GlobalWarming.getInstance().getLogger().info(String.format("Loading %d players...", returnList.size())); 86 | for (GPlayer gPlayer : returnList) { 87 | if (!uuidMap.containsKey(gPlayer.getUniqueId())) { 88 | uuidMap.put(gPlayer.getUniqueId(), gPlayer.getUuid()); 89 | } 90 | 91 | if (!players.containsKey(gPlayer.getUuid())) { 92 | players.put(gPlayer.getUuid(), gPlayer); 93 | } 94 | } 95 | } 96 | }.runTask(GlobalWarming.getInstance()); 97 | } else { 98 | System.out.printf("Selection returned %d players.%n", returnList.size()); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/config/WorldConfig.java: -------------------------------------------------------------------------------- 1 | package net.porillo.config; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.effect.api.ClimateEffectType; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.World; 8 | 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | import java.util.UUID; 12 | 13 | import static net.porillo.engine.models.ScoreTempModel.CarbonSensitivity; 14 | 15 | @Getter 16 | public class WorldConfig extends ConfigLoader { 17 | 18 | private final UUID worldId; 19 | private boolean enabled; 20 | private Set enabledEffects; 21 | private UUID associatedWorldId; 22 | private CarbonSensitivity sensitivity; 23 | private double blastFurnaceMultiplier; 24 | private double methaneTicksLivedModifier; 25 | private boolean bonemealReductionAllowed; 26 | private double bonemealReductionModifier; 27 | 28 | public WorldConfig(UUID worldId) { 29 | super(String.format("%s.yml", Bukkit.getWorld(worldId).getName()), "world.yml"); 30 | super.saveIfNotExist(); 31 | this.worldId = worldId; 32 | super.load(); 33 | } 34 | 35 | public String getName() { 36 | return super.getFileName().substring(0, super.getFileName().indexOf(".")); 37 | } 38 | 39 | @Override 40 | protected void loadKeys() { 41 | sensitivity = CarbonSensitivity.LOW; 42 | String carbonSensitivity = conf.getString("carbonSensitivity"); 43 | if (carbonSensitivity != null && !carbonSensitivity.isEmpty()) { 44 | try { 45 | sensitivity = CarbonSensitivity.valueOf(carbonSensitivity); 46 | } catch (Exception e) { 47 | GlobalWarming.getInstance().getLogger().warning( 48 | String.format( 49 | "Unknown carbon sensitivity for: [%s], defaulting to [%s]", 50 | getDisplayName(worldId), 51 | sensitivity)); 52 | } 53 | } 54 | 55 | this.enabled = this.conf.getBoolean("enabled"); 56 | String association = this.conf.getString("association", "world"); 57 | 58 | if (association != null) { 59 | try { 60 | World associatedWorld = Bukkit.getWorld(association); 61 | 62 | if (associatedWorld != null) { 63 | this.associatedWorldId = associatedWorld.getUID(); 64 | } else { 65 | GlobalWarming.getInstance().getLogger().severe("Associated world not found in file: " + getDisplayName(worldId)); 66 | this.associatedWorldId = worldId; 67 | } 68 | } catch (NullPointerException ex) { 69 | ex.printStackTrace(); 70 | } 71 | } 72 | 73 | this.enabledEffects = new HashSet<>(); 74 | this.blastFurnaceMultiplier = this.conf.getDouble("blastFurnaceMultiplier", 1.2); 75 | this.methaneTicksLivedModifier = this.conf.getDouble("methaneTicksLivedModifier", 0.01); 76 | this.bonemealReductionAllowed = this.conf.getBoolean("bonemealReductionAllowed", true); 77 | this.bonemealReductionModifier = this.conf.getDouble("bonemealReductionModifier", 0.5); 78 | 79 | for (String effect : this.conf.getStringList("enabledEffects")) { 80 | try { 81 | this.enabledEffects.add(ClimateEffectType.valueOf(effect)); 82 | } catch (IllegalArgumentException ex) { 83 | GlobalWarming.getInstance().getLogger().severe(String.format( 84 | "Could not load effect: [%s] for world: [%s]", 85 | effect, 86 | getDisplayName(worldId))); 87 | } 88 | } 89 | } 90 | 91 | @Override 92 | protected void reload() { 93 | this.enabledEffects.clear(); 94 | super.reload(); 95 | } 96 | 97 | public static String getDisplayName(UUID worldId) { 98 | String worldName = "UNKNOWN"; 99 | if (worldId != null) { 100 | World world = Bukkit.getWorld(worldId); 101 | if (world != null) { 102 | worldName = world.getName(); 103 | } 104 | } 105 | 106 | return worldName; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/neutral/Weather.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect.neutral; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.reflect.TypeToken; 5 | import lombok.Getter; 6 | import net.porillo.GlobalWarming; 7 | import net.porillo.effect.ClimateData; 8 | import net.porillo.effect.api.ClimateEffectType; 9 | import net.porillo.effect.api.ScheduleClimateEffect; 10 | import net.porillo.engine.ClimateEngine; 11 | import net.porillo.engine.api.Distribution; 12 | import net.porillo.engine.api.WorldClimateEngine; 13 | import org.bukkit.Bukkit; 14 | import org.bukkit.Location; 15 | import org.bukkit.World; 16 | import org.bukkit.entity.Player; 17 | 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.ThreadLocalRandom; 22 | 23 | @ClimateData(type = ClimateEffectType.WEATHER) 24 | public class Weather extends ScheduleClimateEffect { 25 | 26 | public enum WeatherData {STORM, THUNDER, STRIKE_PLAYER, DURATION} 27 | 28 | @Getter public HashMap weatherDistribution; 29 | 30 | /** 31 | * Determine if a weather effect is allowed 32 | */ 33 | private boolean isAllowed(WorldClimateEngine worldEngine, WeatherData data) { 34 | boolean isAllowed = false; 35 | Distribution distribution = weatherDistribution.get(data); 36 | if (distribution != null && worldEngine != null && worldEngine.isEffectEnabled(ClimateEffectType.WEATHER)) { 37 | final double random = GlobalWarming.getInstance().getRandom().nextDouble(); 38 | final double chance = distribution.getValue(worldEngine.getTemperature()); 39 | isAllowed = random <= (chance / 100.f); 40 | } 41 | 42 | return isAllowed; 43 | } 44 | 45 | /** 46 | * Periodically create storms based on the current temperature 47 | */ 48 | @Override 49 | public void run() { 50 | for (World world : Bukkit.getWorlds()) { 51 | //Storm: 52 | WorldClimateEngine worldEngine = ClimateEngine.getInstance().getClimateEngine(world.getUID()); 53 | if (isAllowed(worldEngine, WeatherData.STORM)) { 54 | world.setStorm(true); 55 | int duration = (int) weatherDistribution.get(WeatherData.DURATION).getValue(worldEngine.getTemperature()); 56 | world.setWeatherDuration(duration); 57 | } 58 | 59 | //Thunder (if storming): 60 | if (world.hasStorm() && isAllowed(worldEngine, WeatherData.THUNDER)) { 61 | world.setThundering(true); 62 | int duration = (int) weatherDistribution.get(WeatherData.DURATION).getValue(worldEngine.getTemperature()); 63 | world.setThunderDuration(duration); 64 | } 65 | 66 | //Lightning strike (if storming): 67 | // - Random player selected (must be outdoors) 68 | if (world.hasStorm() && isAllowed(worldEngine, WeatherData.STRIKE_PLAYER)) { 69 | Location location = getOutdoorPlayerLocation(world); 70 | if (location != null) { 71 | world.strikeLightning(location); 72 | } 73 | } 74 | } 75 | } 76 | 77 | /** 78 | * Get a random player's location (must be outdoors, specifically: no block overhead) 79 | */ 80 | private Location getOutdoorPlayerLocation(World world) { 81 | Location location = null; 82 | List players = world.getPlayers(); 83 | if (players.size() > 0) { 84 | Player player = players.get(ThreadLocalRandom.current().nextInt(0, players.size())); 85 | if (world.getHighestBlockAt(player.getLocation()).getY() < player.getLocation().getY()) { 86 | location = player.getLocation(); 87 | } 88 | } 89 | 90 | return location; 91 | } 92 | 93 | /** 94 | * Load the weather distribution model 95 | */ 96 | @Override 97 | public void setJsonModel(JsonObject jsonModel) { 98 | super.setJsonModel(jsonModel); 99 | this.weatherDistribution = 100 | GlobalWarming.getInstance().getGson().fromJson( 101 | jsonModel.get("distribution"), 102 | new TypeToken>() { 103 | }.getType()); 104 | 105 | if (this.weatherDistribution == null) { 106 | unregister(); 107 | } else { 108 | setPeriod(jsonModel.get("interval").getAsInt()); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/effect/EffectEngine.java: -------------------------------------------------------------------------------- 1 | package net.porillo.effect; 2 | 3 | import com.google.gson.JsonObject; 4 | import net.porillo.GlobalWarming; 5 | import net.porillo.effect.api.ClimateEffect; 6 | import net.porillo.effect.api.ClimateEffectType; 7 | import net.porillo.effect.api.ListenerClimateEffect; 8 | import net.porillo.effect.api.ScheduleClimateEffect; 9 | import net.porillo.effect.negative.Fire; 10 | import net.porillo.effect.negative.PermanentSlowness; 11 | import net.porillo.effect.negative.SeaLevelRise; 12 | import net.porillo.effect.negative.formation.IceForm; 13 | import net.porillo.effect.negative.formation.SnowForm; 14 | import net.porillo.effect.neutral.FarmYield; 15 | import net.porillo.effect.neutral.MobSpawningRate; 16 | import net.porillo.effect.neutral.Weather; 17 | import org.bukkit.Bukkit; 18 | import org.bukkit.event.HandlerList; 19 | import org.bukkit.event.Listener; 20 | 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | public class EffectEngine { 25 | 26 | private static EffectEngine effectEngine; 27 | 28 | private HashMap effects = new HashMap<>(); 29 | private HashMap> effectClasses = new HashMap<>(); 30 | private EffectModel model; 31 | 32 | private EffectEngine() { 33 | registerClass(SeaLevelRise.class); 34 | registerClass(MobSpawningRate.class); 35 | registerClass(Weather.class); 36 | registerClass(FarmYield.class); 37 | registerClass(SnowForm.class); 38 | registerClass(IceForm.class); 39 | registerClass(PermanentSlowness.class); 40 | registerClass(Fire.class); 41 | 42 | this.model = new EffectModel(); 43 | 44 | loadEffects(); 45 | } 46 | 47 | private void loadEffects() { 48 | for (Map.Entry> entry : effectClasses.entrySet()) { 49 | if (model.isEnabled(entry.getKey())) { 50 | JsonObject data = model.getEffect(entry.getKey()); 51 | ClimateEffect effect; 52 | try { 53 | effect = entry.getValue().getConstructor().newInstance(); 54 | } catch (ReflectiveOperationException e) { 55 | e.printStackTrace(); 56 | continue; 57 | } 58 | 59 | effects.put(entry.getKey(), effect); 60 | if (entry.getValue().getAnnotation(ClimateData.class).provideModel()) { 61 | effect.setJsonModel(data.getAsJsonObject("model")); 62 | } 63 | 64 | if (effect instanceof Listener) { 65 | Bukkit.getPluginManager().registerEvents((Listener) effect, GlobalWarming.getInstance()); 66 | } 67 | 68 | if (effect instanceof ScheduleClimateEffect) { 69 | ScheduleClimateEffect runnable = (ScheduleClimateEffect) effect; 70 | runnable.setTaskId(Bukkit.getScheduler().runTaskTimer(GlobalWarming.getInstance(), runnable, 0, runnable.getPeriod()).getTaskId()); 71 | } 72 | 73 | effect.onPluginEnable(); 74 | } 75 | } 76 | } 77 | 78 | public void unloadEffects() { 79 | for (ClimateEffect climateFffect : effects.values()) { 80 | climateFffect.onPluginDisable(); 81 | } 82 | } 83 | 84 | private void registerClass(Class clazz) { 85 | ClimateData climateData = clazz.getAnnotation(ClimateData.class); 86 | if (climateData != null) { 87 | effectClasses.put(climateData.type(), clazz); 88 | } 89 | } 90 | 91 | public void unregisterEffect(ClimateEffectType effectType) { 92 | ClimateEffect effect = effects.get(effectType); 93 | if (effect instanceof Listener) { 94 | HandlerList.unregisterAll((ListenerClimateEffect) effect); 95 | } 96 | if (effect instanceof ScheduleClimateEffect) { 97 | Bukkit.getScheduler().cancelTask(((ScheduleClimateEffect) effect).getTaskId()); 98 | } 99 | 100 | effectClasses.remove(effectType); 101 | effects.remove(effectType); 102 | } 103 | 104 | public T getEffect(Class clazz, ClimateEffectType effectType) { 105 | return clazz.cast(effects.get(effectType)); 106 | } 107 | 108 | public static EffectEngine getInstance() { 109 | if (effectEngine == null) { 110 | effectEngine = new EffectEngine(); 111 | } 112 | 113 | return effectEngine; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/net/porillo/config/Lang.java: -------------------------------------------------------------------------------- 1 | package net.porillo.config; 2 | 3 | import lombok.Getter; 4 | import net.porillo.GlobalWarming; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.configuration.ConfigurationSection; 7 | 8 | import java.text.MessageFormat; 9 | 10 | public enum Lang { 11 | /** 12 | * See lang.yml for translations 13 | */ 14 | ALERT_SUBSCRIBE(""), 15 | ALERT_UNSUBSCRIBE(""), 16 | ALERT_TREEREDUCE(""), 17 | ALERT_TREEREDUCEBONEMEAL(""), 18 | ALERT_BURNCONTRIB(""), 19 | ALERT_FARMCONTRIB(""), 20 | BOUNTY_PLAYER(""), 21 | BOUNTY_HUNTER(""), 22 | BOUNTY_BLOCKS(""), 23 | BOUNTY_BLOCKSREQUIRED(""), 24 | BOUNTY_REWARD(""), 25 | BOUNTY_REWARDREQUIRED(""), 26 | BOUNTY_JOIN(""), 27 | BOUNTY_JOINTOOLTIP(""), 28 | BOUNTY_TITLE(""), 29 | BOUNTY_ERROR(""), 30 | BOUNTY_ALREADYHUNTING(""), 31 | BOUNTY_NOTFOUND(""), 32 | BOUNTY_ANOTHERPLAYER(""), 33 | BOUNTY_BOUNTYOWNER(""), 34 | BOUNTY_CREATED(""), 35 | BOUNTY_NOTCREATED(""), 36 | BOUNTY_MAXCREATED(""), 37 | BOUNTY_ACCEPTED(""), 38 | BOUNTY_ACCEPTEDBY(""), 39 | BOUNTY_COMPLETED(""), 40 | BOUNTY_COMPLETEDBY(""), 41 | BOUNTY_NOTJOINED(""), 42 | BOUNTY_ABANDONED(""), 43 | BOUNTY_ABANDONEDBY(""), 44 | BOUNTY_CANCELLED(""), 45 | ENGINE_DISABLED(""), 46 | ENGINE_NOTFOUND(""), 47 | GENERIC_PERMISSION(""), 48 | GENERIC_INVALIDARGS(""), 49 | GENERIC_SPAM(""), 50 | GENERIC_INVENTORYFULL(""), 51 | GENERIC_PLAYERONLY(""), 52 | NOTIFICATION_DEFAULT_LOW(""), 53 | NOTIFICATION_DEFAULT_OK(""), 54 | NOTIFICATION_DEFAULT_HIGH(""), 55 | NOTIFICATION_FARM_LOW(""), 56 | NOTIFICATION_FARM_OK(""), 57 | NOTIFICATION_FARM_HIGH(""), 58 | NOTIFICATION_FIRE_LOW(""), 59 | NOTIFICATION_FIRE_OK(""), 60 | NOTIFICATION_FIRE_HIGH(""), 61 | NOTIFICATION_ICE_LOW(""), 62 | NOTIFICATION_ICE_OK(""), 63 | NOTIFICATION_ICE_HIGH(""), 64 | NOTIFICATION_MOB_LOW(""), 65 | NOTIFICATION_MOB_OK(""), 66 | NOTIFICATION_MOB_HIGH(""), 67 | NOTIFICATION_WEATHER_LOW(""), 68 | NOTIFICATION_WEATHER_OK(""), 69 | NOTIFICATION_WEATHER_HIGH(""), 70 | NOTIFICATION_SEALEVEL_LOW(""), 71 | NOTIFICATION_SEALEVEL_OK(""), 72 | NOTIFICATION_SEALEVEL_HIGH(""), 73 | NOTIFICATION_SLOWNESS_LOW(""), 74 | NOTIFICATION_SLOWNESS_OK(""), 75 | NOTIFICATION_SLOWNESS_HIGH(""), 76 | NOTIFICATION_SNOW_LOW(""), 77 | NOTIFICATION_SNOW_OK(""), 78 | NOTIFICATION_SNOW_HIGH(""), 79 | SCORE_CHAT(""), 80 | SCORE_TEMPERATURE(""), 81 | TABLE_EMPTY(""), 82 | TEMPERATURE_BALANCED(""), 83 | TEMPERATURE_HIGH(""), 84 | TEMPERATURE_HIGHWITHBOUNTY(""), 85 | TEMPERATURE_AVERAGE(""), 86 | TEMPERATURE_LOW(""), 87 | TOPTABLE_PLAYER(""), 88 | TOPTABLE_INDEX(""), 89 | TOPTABLE_SCORE(""), 90 | TOPTABLE_POLLUTERS(""), 91 | TOPTABLE_PLANTERS(""), 92 | TOPTABLE_ERROR(""), 93 | WIKI_ADDED(""), 94 | WIKI_ALREADYADDED(""), 95 | WIKI_NAME(""), 96 | WIKI_AUTHOR(""), 97 | WIKI_LORE(""), 98 | WIKI_INTRODUCTION(""), 99 | WIKI_SCORES(""), 100 | WIKI_EFFECTS(""), 101 | WIKI_BOUNTY(""), 102 | WIKI_OTHER(""); 103 | 104 | private String def; 105 | private static LangConfig langConfig; 106 | 107 | Lang(String def) { 108 | this.def = def; 109 | } 110 | 111 | public String get() { 112 | return color(getRaw()); 113 | } 114 | 115 | public String get(Object... args) { 116 | return color(MessageFormat.format(getRaw(), args)); 117 | } 118 | 119 | public String getRaw() { 120 | return langConfig.getLangConf().getString(getPath(), getDef()); 121 | } 122 | 123 | public String getPath() { 124 | return name().replace('_', '.'); 125 | } 126 | 127 | public String getDef() { 128 | return def; 129 | } 130 | 131 | public static String color(String string) { 132 | return ChatColor.translateAlternateColorCodes('&', string); 133 | } 134 | 135 | private static class LangConfig extends ConfigLoader { 136 | 137 | @Getter 138 | private String locale; 139 | 140 | private LangConfig() { 141 | super("lang.yml"); 142 | super.saveIfNotExist(); 143 | super.load(); 144 | 145 | GlobalWarming.getInstance().getLogger().info(String.format("Lang loaded: [%s]", locale)); 146 | } 147 | 148 | private ConfigurationSection getLangConf() { 149 | return conf.getConfigurationSection("lang"); 150 | } 151 | 152 | @Override 153 | protected void loadKeys() { 154 | locale = conf.getString("locale", "en-US"); 155 | } 156 | 157 | } 158 | 159 | public static void init() { 160 | langConfig = new LangConfig(); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/resources/models/fuelModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "JUKEBOX": 2.0, 3 | "JUNGLE_BUTTON": 2.0, 4 | "WHITE_BANNER": 2.0, 5 | "WHITE_CARPET": 2.0, 6 | "GREEN_CARPET": 2.0, 7 | "RED_CARPET": 2.0, 8 | "COMPOSTER": 2.0, 9 | "STRIPPED_BIRCH_WOOD": 2.0, 10 | "NOTE_BLOCK": 2.0, 11 | "STRIPPED_SPRUCE_WOOD": 2.0, 12 | "JUNGLE_TRAPDOOR": 2.0, 13 | "SPRUCE_FENCE": 2.0, 14 | "RED_BANNER": 2.0, 15 | "SPRUCE_TRAPDOOR": 2.0, 16 | "GRAY_BANNER": 2.0, 17 | "ACACIA_DOOR": 2.0, 18 | "OAK_TRAPDOOR": 2.0, 19 | "LAVA_BUCKET": 100.0, 20 | "BUCKET": 0.0, 21 | "STRIPPED_ACACIA_LOG": 2.0, 22 | "SPRUCE_WOOD": 2.0, 23 | "DARK_OAK_SAPLING": 2.0, 24 | "LIGHT_GRAY_WOOL": 2.0, 25 | "STRIPPED_DARK_OAK_WOOD": 2.0, 26 | "BOWL": 2.0, 27 | "PINK_CARPET": 2.0, 28 | "BROWN_CARPET": 2.0, 29 | "JUNGLE_PRESSURE_PLATE": 2.0, 30 | "LIGHT_BLUE_BANNER": 2.0, 31 | "ACACIA_FENCE_GATE": 2.0, 32 | "DAYLIGHT_DETECTOR": 2.0, 33 | "ACACIA_STAIRS": 2.0, 34 | "BLUE_BANNER": 2.0, 35 | "ACACIA_FENCE": 2.0, 36 | "BLUE_WOOL": 2.0, 37 | "LIME_BANNER": 2.0, 38 | "SPRUCE_PLANKS": 2.0, 39 | "WOODEN_PICKAXE": 2.0, 40 | "LADDER": 2.0, 41 | "DARK_OAK_PRESSURE_PLATE": 2.0, 42 | "ACACIA_SLAB": 2.0, 43 | "ORANGE_BANNER": 2.0, 44 | "STRIPPED_OAK_LOG": 2.0, 45 | "JUNGLE_FENCE_GATE": 2.0, 46 | "SPRUCE_SLAB": 2.0, 47 | "PURPLE_BANNER": 2.0, 48 | "SPRUCE_FENCE_GATE": 2.0, 49 | "JUNGLE_WOOD": 2.0, 50 | "OAK_PRESSURE_PLATE": 2.0, 51 | "PURPLE_WOOL": 2.0, 52 | "DARK_OAK_TRAPDOOR": 2.0, 53 | "JUNGLE_SLAB": 2.0, 54 | "WOODEN_HOE": 2.0, 55 | "STICK": 2.0, 56 | "LIGHT_GRAY_BANNER": 2.0, 57 | "MAGENTA_BANNER": 2.0, 58 | "BIRCH_PRESSURE_PLATE": 2.0, 59 | "LIGHT_BLUE_CARPET": 2.0, 60 | "BIRCH_BUTTON": 2.0, 61 | "LIGHT_BLUE_WOOL": 2.0, 62 | "ORANGE_CARPET": 2.0, 63 | "SIGN": 2.0, 64 | "COAL_BLOCK": 80.0, 65 | "JUNGLE_DOOR": 2.0, 66 | "CYAN_BANNER": 2.0, 67 | "STRIPPED_OAK_WOOD": 2.0, 68 | "ACACIA_TRAPDOOR": 2.0, 69 | "GRAY_WOOL": 2.0, 70 | "LIME_WOOL": 2.0, 71 | "SPRUCE_BOAT": 2.0, 72 | "BIRCH_BOAT": 2.0, 73 | "CHARCOAL": 8.0, 74 | "BIRCH_WOOD": 2.0, 75 | "BROWN_BANNER": 2.0, 76 | "BLAZE_ROD": 12.0, 77 | "BIRCH_LOG": 2.0, 78 | "BLACK_CARPET": 2.0, 79 | "JUNGLE_LOG": 2.0, 80 | "RED_WOOL": 2.0, 81 | "JUNGLE_BOAT": 2.0, 82 | "JUNGLE_SAPLING": 2.0, 83 | "SPRUCE_SAPLING": 2.0, 84 | "SPRUCE_STAIRS": 2.0, 85 | "DARK_OAK_BUTTON": 2.0, 86 | "WHITE_WOOL": 2.0, 87 | "CRAFTING_TABLE": 2.0, 88 | "DARK_OAK_DOOR": 2.0, 89 | "FISHING_ROD": 2.0, 90 | "JUNGLE_STAIRS": 2.0, 91 | "GREEN_BANNER": 2.0, 92 | "BOW": 2.0, 93 | "LIGHT_GRAY_CARPET": 2.0, 94 | "BLUE_CARPET": 2.0, 95 | "PINK_WOOL": 2.0, 96 | "BIRCH_STAIRS": 2.0, 97 | "STRIPPED_SPRUCE_LOG": 2.0, 98 | "BROWN_WOOL": 2.0, 99 | "GRAY_CARPET": 2.0, 100 | "JUNGLE_FENCE": 2.0, 101 | "BIRCH_PLANKS": 2.0, 102 | "ACACIA_PLANKS": 2.0, 103 | "OAK_FENCE_GATE": 2.0, 104 | "ACACIA_BUTTON": 2.0, 105 | "OAK_BUTTON": 2.0, 106 | "OAK_SLAB": 2.0, 107 | "STRIPPED_ACACIA_WOOD": 2.0, 108 | "OAK_STAIRS": 2.0, 109 | "DARK_OAK_BOAT": 2.0, 110 | "DARK_OAK_FENCE_GATE": 2.0, 111 | "BIRCH_TRAPDOOR": 2.0, 112 | "OAK_BOAT": 2.0, 113 | "YELLOW_BANNER": 2.0, 114 | "STRIPPED_JUNGLE_WOOD": 2.0, 115 | "WOODEN_AXE": 2.0, 116 | "OAK_DOOR": 2.0, 117 | "GREEN_WOOL": 2.0, 118 | "MAGENTA_WOOL": 2.0, 119 | "YELLOW_WOOL": 2.0, 120 | "YELLOW_CARPET": 2.0, 121 | "WOODEN_SHOVEL": 2.0, 122 | "SPRUCE_DOOR": 2.0, 123 | "BLACK_BANNER": 2.0, 124 | "BIRCH_FENCE": 2.0, 125 | "TRAPPED_CHEST": 2.0, 126 | "BIRCH_SAPLING": 2.0, 127 | "ACACIA_PRESSURE_PLATE": 2.0, 128 | "MAGENTA_CARPET": 2.0, 129 | "OAK_SAPLING": 2.0, 130 | "OAK_PLANKS": 2.0, 131 | "COAL": 8.0, 132 | "OAK_LOG": 2.0, 133 | "ACACIA_WOOD": 2.0, 134 | "BIRCH_SLAB": 2.0, 135 | "OAK_WOOD": 2.0, 136 | "ACACIA_LOG": 2.0, 137 | "DARK_OAK_FENCE": 2.0, 138 | "SPRUCE_PRESSURE_PLATE": 2.0, 139 | "JUNGLE_PLANKS": 2.0, 140 | "PURPLE_CARPET": 2.0, 141 | "STRIPPED_DARK_OAK_LOG": 2.0, 142 | "CYAN_WOOL": 2.0, 143 | "ACACIA_BOAT": 2.0, 144 | "OAK_FENCE": 2.0, 145 | "SPRUCE_BUTTON": 2.0, 146 | "DARK_OAK_PLANKS": 2.0, 147 | "LIME_CARPET": 2.0, 148 | "DARK_OAK_STAIRS": 2.0, 149 | "CYAN_CARPET": 2.0, 150 | "WOODEN_SWORD": 2.0, 151 | "BIRCH_DOOR": 2.0, 152 | "BOOKSHELF": 2.0, 153 | "DARK_OAK_WOOD": 2.0, 154 | "DRIED_KELP_BLOCK": 20.0, 155 | "SPRUCE_LOG": 2.0, 156 | "STRIPPED_BIRCH_LOG": 2.0, 157 | "DARK_OAK_LOG": 2.0, 158 | "DARK_OAK_SLAB": 2.0, 159 | "ORANGE_WOOL": 2.0, 160 | "STRIPPED_JUNGLE_LOG": 2.0, 161 | "ACACIA_SAPLING": 2.0, 162 | "PINK_BANNER": 2.0, 163 | "BLACK_WOOL": 2.0, 164 | "CHEST": 2.0, 165 | "BIRCH_FENCE_GATE": 2.0, 166 | "ACACIA_SIGN": 2.0, 167 | "BIRCH_SIGN": 2.0, 168 | "DARK_OAK_SIGN": 2.0, 169 | "JUNGLE_SIGN": 2.0, 170 | "OAK_SIGN": 2.0, 171 | "SPRUCE_SIGN": 2.0, 172 | "BAMBOO": 1.0, 173 | "BARREL": 8.0, 174 | "LEGACY_WOOD": 2.0, 175 | "LEGACY_SAPLING": 2.0, 176 | "LEGACY_LOG": 2.0, 177 | "DEAD_BUSH": 2.0, 178 | "CARTOGRAPHY_TABLE": 8.0, 179 | "LECTERN": 8.0, 180 | "LOOM": 8.0, 181 | "FLETCHING_TABLE": 8.0, 182 | "SMITHING_TABLE": 8.0, 183 | "SCAFFOLDING": 4.0, 184 | "CROSSBOW": 2.0 185 | } 186 | -------------------------------------------------------------------------------- /src/test/resources/models/world/fuelModel.json: -------------------------------------------------------------------------------- 1 | { 2 | "JUKEBOX": 2.0, 3 | "JUNGLE_BUTTON": 2.0, 4 | "WHITE_BANNER": 2.0, 5 | "WHITE_CARPET": 2.0, 6 | "GREEN_CARPET": 2.0, 7 | "RED_CARPET": 2.0, 8 | "COMPOSTER": 2.0, 9 | "STRIPPED_BIRCH_WOOD": 2.0, 10 | "NOTE_BLOCK": 2.0, 11 | "STRIPPED_SPRUCE_WOOD": 2.0, 12 | "JUNGLE_TRAPDOOR": 2.0, 13 | "SPRUCE_FENCE": 2.0, 14 | "RED_BANNER": 2.0, 15 | "SPRUCE_TRAPDOOR": 2.0, 16 | "GRAY_BANNER": 2.0, 17 | "ACACIA_DOOR": 2.0, 18 | "OAK_TRAPDOOR": 2.0, 19 | "LAVA_BUCKET": 100.0, 20 | "BUCKET": 0.0, 21 | "STRIPPED_ACACIA_LOG": 2.0, 22 | "SPRUCE_WOOD": 2.0, 23 | "DARK_OAK_SAPLING": 2.0, 24 | "LIGHT_GRAY_WOOL": 2.0, 25 | "STRIPPED_DARK_OAK_WOOD": 2.0, 26 | "BOWL": 2.0, 27 | "PINK_CARPET": 2.0, 28 | "BROWN_CARPET": 2.0, 29 | "JUNGLE_PRESSURE_PLATE": 2.0, 30 | "LIGHT_BLUE_BANNER": 2.0, 31 | "ACACIA_FENCE_GATE": 2.0, 32 | "DAYLIGHT_DETECTOR": 2.0, 33 | "ACACIA_STAIRS": 2.0, 34 | "BLUE_BANNER": 2.0, 35 | "ACACIA_FENCE": 2.0, 36 | "BLUE_WOOL": 2.0, 37 | "LIME_BANNER": 2.0, 38 | "SPRUCE_PLANKS": 2.0, 39 | "WOODEN_PICKAXE": 2.0, 40 | "LADDER": 2.0, 41 | "DARK_OAK_PRESSURE_PLATE": 2.0, 42 | "ACACIA_SLAB": 2.0, 43 | "ORANGE_BANNER": 2.0, 44 | "STRIPPED_OAK_LOG": 2.0, 45 | "JUNGLE_FENCE_GATE": 2.0, 46 | "SPRUCE_SLAB": 2.0, 47 | "PURPLE_BANNER": 2.0, 48 | "SPRUCE_FENCE_GATE": 2.0, 49 | "JUNGLE_WOOD": 2.0, 50 | "OAK_PRESSURE_PLATE": 2.0, 51 | "PURPLE_WOOL": 2.0, 52 | "DARK_OAK_TRAPDOOR": 2.0, 53 | "JUNGLE_SLAB": 2.0, 54 | "WOODEN_HOE": 2.0, 55 | "STICK": 2.0, 56 | "LIGHT_GRAY_BANNER": 2.0, 57 | "MAGENTA_BANNER": 2.0, 58 | "BIRCH_PRESSURE_PLATE": 2.0, 59 | "LIGHT_BLUE_CARPET": 2.0, 60 | "BIRCH_BUTTON": 2.0, 61 | "LIGHT_BLUE_WOOL": 2.0, 62 | "ORANGE_CARPET": 2.0, 63 | "SIGN": 2.0, 64 | "COAL_BLOCK": 80.0, 65 | "JUNGLE_DOOR": 2.0, 66 | "CYAN_BANNER": 2.0, 67 | "STRIPPED_OAK_WOOD": 2.0, 68 | "ACACIA_TRAPDOOR": 2.0, 69 | "GRAY_WOOL": 2.0, 70 | "LIME_WOOL": 2.0, 71 | "SPRUCE_BOAT": 2.0, 72 | "BIRCH_BOAT": 2.0, 73 | "CHARCOAL": 8.0, 74 | "BIRCH_WOOD": 2.0, 75 | "BROWN_BANNER": 2.0, 76 | "BLAZE_ROD": 12.0, 77 | "BIRCH_LOG": 2.0, 78 | "BLACK_CARPET": 2.0, 79 | "JUNGLE_LOG": 2.0, 80 | "RED_WOOL": 2.0, 81 | "JUNGLE_BOAT": 2.0, 82 | "JUNGLE_SAPLING": 2.0, 83 | "SPRUCE_SAPLING": 2.0, 84 | "SPRUCE_STAIRS": 2.0, 85 | "DARK_OAK_BUTTON": 2.0, 86 | "WHITE_WOOL": 2.0, 87 | "CRAFTING_TABLE": 2.0, 88 | "DARK_OAK_DOOR": 2.0, 89 | "FISHING_ROD": 2.0, 90 | "JUNGLE_STAIRS": 2.0, 91 | "GREEN_BANNER": 2.0, 92 | "BOW": 2.0, 93 | "LIGHT_GRAY_CARPET": 2.0, 94 | "BLUE_CARPET": 2.0, 95 | "PINK_WOOL": 2.0, 96 | "BIRCH_STAIRS": 2.0, 97 | "STRIPPED_SPRUCE_LOG": 2.0, 98 | "BROWN_WOOL": 2.0, 99 | "GRAY_CARPET": 2.0, 100 | "JUNGLE_FENCE": 2.0, 101 | "BIRCH_PLANKS": 2.0, 102 | "ACACIA_PLANKS": 2.0, 103 | "OAK_FENCE_GATE": 2.0, 104 | "ACACIA_BUTTON": 2.0, 105 | "OAK_BUTTON": 2.0, 106 | "OAK_SLAB": 2.0, 107 | "STRIPPED_ACACIA_WOOD": 2.0, 108 | "OAK_STAIRS": 2.0, 109 | "DARK_OAK_BOAT": 2.0, 110 | "DARK_OAK_FENCE_GATE": 2.0, 111 | "BIRCH_TRAPDOOR": 2.0, 112 | "OAK_BOAT": 2.0, 113 | "YELLOW_BANNER": 2.0, 114 | "STRIPPED_JUNGLE_WOOD": 2.0, 115 | "WOODEN_AXE": 2.0, 116 | "OAK_DOOR": 2.0, 117 | "GREEN_WOOL": 2.0, 118 | "MAGENTA_WOOL": 2.0, 119 | "YELLOW_WOOL": 2.0, 120 | "YELLOW_CARPET": 2.0, 121 | "WOODEN_SHOVEL": 2.0, 122 | "SPRUCE_DOOR": 2.0, 123 | "BLACK_BANNER": 2.0, 124 | "BIRCH_FENCE": 2.0, 125 | "TRAPPED_CHEST": 2.0, 126 | "BIRCH_SAPLING": 2.0, 127 | "ACACIA_PRESSURE_PLATE": 2.0, 128 | "MAGENTA_CARPET": 2.0, 129 | "OAK_SAPLING": 2.0, 130 | "OAK_PLANKS": 2.0, 131 | "COAL": 8.0, 132 | "OAK_LOG": 2.0, 133 | "ACACIA_WOOD": 2.0, 134 | "BIRCH_SLAB": 2.0, 135 | "OAK_WOOD": 2.0, 136 | "ACACIA_LOG": 2.0, 137 | "DARK_OAK_FENCE": 2.0, 138 | "SPRUCE_PRESSURE_PLATE": 2.0, 139 | "JUNGLE_PLANKS": 2.0, 140 | "PURPLE_CARPET": 2.0, 141 | "STRIPPED_DARK_OAK_LOG": 2.0, 142 | "CYAN_WOOL": 2.0, 143 | "ACACIA_BOAT": 2.0, 144 | "OAK_FENCE": 2.0, 145 | "SPRUCE_BUTTON": 2.0, 146 | "DARK_OAK_PLANKS": 2.0, 147 | "LIME_CARPET": 2.0, 148 | "DARK_OAK_STAIRS": 2.0, 149 | "CYAN_CARPET": 2.0, 150 | "WOODEN_SWORD": 2.0, 151 | "BIRCH_DOOR": 2.0, 152 | "BOOKSHELF": 2.0, 153 | "DARK_OAK_WOOD": 2.0, 154 | "DRIED_KELP_BLOCK": 20.0, 155 | "SPRUCE_LOG": 2.0, 156 | "STRIPPED_BIRCH_LOG": 2.0, 157 | "DARK_OAK_LOG": 2.0, 158 | "DARK_OAK_SLAB": 2.0, 159 | "ORANGE_WOOL": 2.0, 160 | "STRIPPED_JUNGLE_LOG": 2.0, 161 | "ACACIA_SAPLING": 2.0, 162 | "PINK_BANNER": 2.0, 163 | "BLACK_WOOL": 2.0, 164 | "CHEST": 2.0, 165 | "BIRCH_FENCE_GATE": 2.0, 166 | "ACACIA_SIGN": 2.0, 167 | "BIRCH_SIGN": 2.0, 168 | "DARK_OAK_SIGN": 2.0, 169 | "JUNGLE_SIGN": 2.0, 170 | "OAK_SIGN": 2.0, 171 | "SPRUCE_SIGN": 2.0, 172 | "BAMBOO": 1.0, 173 | "BARREL": 8.0, 174 | "LEGACY_WOOD": 2.0, 175 | "LEGACY_SAPLING": 2.0, 176 | "LEGACY_LOG": 2.0, 177 | "DEAD_BUSH": 2.0, 178 | "CARTOGRAPHY_TABLE": 8.0, 179 | "LECTERN": 8.0, 180 | "LOOM": 8.0, 181 | "FLETCHING_TABLE": 8.0, 182 | "SMITHING_TABLE": 8.0, 183 | "SCAFFOLDING": 4.0, 184 | "CROSSBOW": 2.0 185 | } 186 | --------------------------------------------------------------------------------