├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── art
├── hidden-entities.gif
├── meltpoint-banner2.png
├── nether-view-demo.gif
└── view dist.png
├── pom.xml
└── src
└── main
├── java
└── me
│ └── gorgeousone
│ └── netherview
│ ├── ConfigSettings.java
│ ├── NetherViewPlugin.java
│ ├── blockcache
│ ├── BlockCache.java
│ ├── BlockCacheFactory.java
│ ├── ProjectionCache.java
│ ├── Transform.java
│ └── TransformFactory.java
│ ├── bstats
│ └── Metrics.java
│ ├── cmdframework
│ ├── argument
│ │ ├── ArgType.java
│ │ ├── ArgValue.java
│ │ └── Argument.java
│ ├── command
│ │ ├── ArgCommand.java
│ │ ├── BasicCommand.java
│ │ └── ParentCommand.java
│ └── handlers
│ │ ├── CommandCompleter.java
│ │ └── CommandHandler.java
│ ├── commmands
│ ├── FlipPortalCommand.java
│ ├── ListPortalsCommand.java
│ ├── PortalInfoCommand.java
│ ├── ReloadCommand.java
│ ├── ToggleDebugCommand.java
│ ├── TogglePortalViewCommand.java
│ └── ToggleWarningsCommand.java
│ ├── customportal
│ ├── CustomPortal.java
│ ├── CustomPortalCreator.java
│ ├── CustomPortalHandler.java
│ ├── CustomPortalSerializer.java
│ ├── PlayerClickListener.java
│ ├── PlayerCuboidSelection.java
│ ├── PlayerSelectionHandler.java
│ └── commands
│ │ ├── CreatePortalCommand.java
│ │ ├── DeletePortalCommand.java
│ │ ├── GetWandCommand.java
│ │ ├── LinkPortalCommand.java
│ │ └── UnlinkPortalCommand.java
│ ├── event
│ ├── PortalLinkEvent.java
│ ├── PortalUnlinkEvent.java
│ └── UnlinkReason.java
│ ├── geometry
│ ├── AxisAlignedRect.java
│ ├── BlockVec.java
│ ├── Cuboid.java
│ ├── Line.java
│ ├── Plane.java
│ └── viewfrustum
│ │ ├── DefinedLine.java
│ │ ├── ViewFrustum.java
│ │ └── ViewFrustumFactory.java
│ ├── handlers
│ ├── EntityVisibilityHandler.java
│ ├── PlayerViewSession.java
│ ├── PortalHandler.java
│ └── ViewHandler.java
│ ├── listeners
│ ├── BlockChangeListener.java
│ ├── PlayerMoveListener.java
│ ├── PlayerQuitListener.java
│ └── PlayerTeleportListener.java
│ ├── message
│ ├── Message.java
│ ├── MessageException.java
│ └── MessageUtils.java
│ ├── packet
│ ├── EntitySpawnPacketFactory1_13.java
│ └── PacketHandler.java
│ ├── portal
│ ├── Portal.java
│ ├── PortalLocator.java
│ ├── PortalSerializer.java
│ ├── PortalSerializer2_1_0.java
│ └── ProjectionEntity.java
│ ├── updatechecks
│ ├── UpdateCheck.java
│ ├── UpdateInfo.java
│ └── VersionResponse.java
│ ├── utils
│ ├── ConfigUtils.java
│ ├── FacingUtils.java
│ ├── NmsUtils.java
│ ├── TeleportUtils.java
│ ├── TimeUtils.java
│ └── VersionUtils.java
│ └── wrapper
│ ├── Axis.java
│ ├── WrappedBoundingBox.java
│ ├── blocktype
│ ├── AquaticBlockType.java
│ ├── BlockType.java
│ └── LegacyBlockType.java
│ └── rotation
│ ├── AquaticRailUtils.java
│ └── RotationUtils.java
└── resources
├── config.yml
├── config2.yml
├── language.yml
└── plugin.yml
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to world '...'
16 | 2. Create portal with size of '...' blocks
17 | 3. Click/Break nearby block
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Console logs**
24 | If relevant, add logs from your server console that might be related to your problem.
25 |
26 | **Screenshots**
27 | If applicable, add screenshots to help explain your problem.
28 |
29 | **Server version and plugin version
30 | The version of Nether View you are using, and the spigot version your server is running on
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
8 | hs_err_pid*
9 |
10 | target/
11 | .idea/
12 | *.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Nether View
4 | A plugin that makes it possible to see the nether through nether portals.
5 | This was inspired by SethBling's [Magic Nether Portal](https://www.youtube.com/watch?v=xewQL6CkMWI).
6 |
7 | 
8 |
9 | ### Installation
10 | The latest releases can be downloaded here: [spigotmc.org](https://www.spigotmc.org/resources/nether-view.78885/)
11 |
12 | ### Compiling
13 | Nether View uses Maven 3 to manage dependencies.
14 |
15 | Required libraries:
16 | - Bukkit
17 | - ProtocolLib
18 |
19 | ### Contributing
20 |
21 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
22 |
23 | ### bStats
24 | [](https://bstats.org/plugin/bukkit/NetherView/7571)
25 |
--------------------------------------------------------------------------------
/art/hidden-entities.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GorgeousOne/NetherView/db9de649a5c6397bfba31704b279eaa9c0de8d0e/art/hidden-entities.gif
--------------------------------------------------------------------------------
/art/meltpoint-banner2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GorgeousOne/NetherView/db9de649a5c6397bfba31704b279eaa9c0de8d0e/art/meltpoint-banner2.png
--------------------------------------------------------------------------------
/art/nether-view-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GorgeousOne/NetherView/db9de649a5c6397bfba31704b279eaa9c0de8d0e/art/nether-view-demo.gif
--------------------------------------------------------------------------------
/art/view dist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GorgeousOne/NetherView/db9de649a5c6397bfba31704b279eaa9c0de8d0e/art/view dist.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | me.gorgeousone
8 | netherview
9 | 3.0.0
10 | jar
11 |
12 | NetherView
13 |
14 |
15 | 1.8
16 | UTF-8
17 |
18 |
19 |
20 | clean package
21 |
22 |
23 | org.apache.maven.plugins
24 | maven-compiler-plugin
25 | 3.7.0
26 |
27 | ${java.version}
28 | ${java.version}
29 |
30 |
31 |
32 | org.apache.maven.plugins
33 | maven-shade-plugin
34 | 3.1.0
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | package
46 |
47 | shade
48 |
49 |
50 | false
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | src/main/resources
59 | true
60 |
61 |
62 |
63 |
64 |
65 |
66 | spigotmc-repo
67 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/
68 |
69 |
70 | dmulloy2-repo
71 | https://repo.dmulloy2.net/nexus/repository/public/
72 |
73 |
74 | CodeMC
75 | https://repo.codemc.org/repository/maven-public
76 |
77 |
78 |
79 |
80 |
81 | org.spigotmc
82 | spigot
83 | 1.13.2-R0.1-SNAPSHOT
84 | provided
85 |
86 |
87 | org.junit.jupiter
88 | junit-jupiter-api
89 | 5.6.1
90 | test
91 |
92 |
93 | com.comphenix.protocol
94 | ProtocolLib
95 | 4.6.0-SNAPSHOT
96 | provided
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/ConfigSettings.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview;
2 |
3 | import me.gorgeousone.netherview.message.MessageUtils;
4 | import me.gorgeousone.netherview.utils.VersionUtils;
5 | import me.gorgeousone.netherview.wrapper.blocktype.BlockType;
6 | import org.bukkit.Bukkit;
7 | import org.bukkit.World;
8 | import org.bukkit.configuration.ConfigurationSection;
9 | import org.bukkit.configuration.file.FileConfiguration;
10 | import org.bukkit.plugin.java.JavaPlugin;
11 |
12 | import java.util.Collection;
13 | import java.util.HashMap;
14 | import java.util.HashSet;
15 | import java.util.List;
16 | import java.util.Set;
17 | import java.util.UUID;
18 | import java.util.logging.Level;
19 |
20 | public class ConfigSettings {
21 |
22 | private final JavaPlugin plugin;
23 |
24 | private int portalProjectionDist;
25 | private int portalDisplayRangeSquared;
26 | private int maxPortalSize;
27 | private HashMap worldBorderBlockTypes;
28 |
29 | private boolean entityHidingEnabled;
30 | private boolean entityViewingEnabled;
31 | private int entityUpdateTicks;
32 |
33 | private boolean warningMessagesEnabled;
34 | private boolean debugMessagesEnabled;
35 |
36 | private boolean allWorldsCanCreateCustomPortals = false;
37 | private Set customPortalWhiteList;
38 | private Set customPortalBlackList;
39 |
40 | private boolean allWorldsCanCreatePortalViews = false;
41 | private Set portalViewWhiteList;
42 | private Set portalViewBlackList;
43 |
44 | private boolean netherPortalsAreFlippedByDefault;
45 | private boolean hidePortalBlocks;
46 | private boolean cancelTeleportWhenLinking;
47 | private boolean instantTeleportEnabled;
48 |
49 | public ConfigSettings(JavaPlugin plugin, FileConfiguration config) {
50 | this.plugin = plugin;
51 | }
52 |
53 | /**
54 | * Returns the approximate "radius" for the projections of the portals. The final side size of a projection will change
55 | * depending to the size of the portal.
56 | */
57 | public int getPortalProjectionDist() {
58 | return portalProjectionDist;
59 | }
60 |
61 | /**
62 | * Returns the squared radius in which the view of a portal will be displayed to players.
63 | */
64 | public int getPortalDisplayRangeSquared() {
65 | return portalDisplayRangeSquared;
66 | }
67 |
68 | public int getMaxPortalSize() {
69 | return maxPortalSize;
70 | }
71 |
72 | public boolean isEntityHidingEnabled() {
73 | return entityHidingEnabled;
74 | }
75 |
76 | public boolean isEntityViewingEnabled() {
77 | return entityViewingEnabled;
78 | }
79 |
80 | public int getEntityUpdateTicks() {
81 | return entityUpdateTicks;
82 | }
83 |
84 | public BlockType getWorldBorderBlockType(World.Environment environment) {
85 | return worldBorderBlockTypes.get(environment);
86 | }
87 |
88 | public boolean canCreateCustomPortals(World world) {
89 |
90 | UUID worldId = world.getUID();
91 | return !customPortalBlackList.contains(worldId) && (allWorldsCanCreateCustomPortals || customPortalWhiteList.contains(worldId));
92 | }
93 |
94 | public boolean canCreatePortalViews(World world) {
95 |
96 | UUID worldId = world.getUID();
97 | return !portalViewBlackList.contains(worldId) && (allWorldsCanCreatePortalViews || portalViewWhiteList.contains(worldId));
98 | }
99 |
100 | public boolean portalsAreFlippedByDefault() {
101 | return netherPortalsAreFlippedByDefault;
102 | }
103 |
104 | /**
105 | * Returns true if hiding the purple portal blocks when seeing a portal view is enabled in the config.
106 | */
107 | public boolean hidePortalBlocksEnabled() {
108 | return hidePortalBlocks;
109 | }
110 |
111 | public boolean isInstantTeleportEnabled() {
112 | return instantTeleportEnabled;
113 | }
114 |
115 | public boolean cancelTeleportWhenLinkingPortalsEnabled() {
116 | return cancelTeleportWhenLinking;
117 | }
118 |
119 | public boolean areWarningMessagesEnabled() {
120 | return warningMessagesEnabled;
121 | }
122 |
123 | public boolean areDebugMessagesEnabled() {
124 | return debugMessagesEnabled;
125 | }
126 |
127 | public void setWarningMessagesEnabled(boolean warningMessagesEnabled) {
128 | this.warningMessagesEnabled = warningMessagesEnabled;
129 | }
130 |
131 | public void setDebugMessagesEnabled(boolean debugMessagesEnabled) {
132 | this.debugMessagesEnabled = debugMessagesEnabled;
133 | }
134 |
135 | public void addVersionSpecificDefaults(FileConfiguration config) {
136 |
137 | if (VersionUtils.IS_LEGACY_SERVER) {
138 | config.addDefault("overworld-border", "stained_clay");
139 | config.addDefault("nether-border", "stained_clay:14");
140 | config.addDefault("end-border", "wool:15");
141 |
142 | } else {
143 | config.addDefault("overworld-border", "white_terracotta");
144 | config.addDefault("nether-border", "red_concrete");
145 | config.addDefault("end-border", "black_concrete");
146 | }
147 | }
148 |
149 | public void loadGeneralSettings(FileConfiguration config) {
150 |
151 | int portalDisplayRange = clamp(config.getInt("portal-display-range"), 2, 128);
152 | portalDisplayRangeSquared = (int) Math.pow(portalDisplayRange, 2);
153 | portalProjectionDist = clamp(config.getInt("portal-projection-distance"), 1, 32);
154 | maxPortalSize = clamp(config.getInt("max-portal-size"), 3, 21);
155 |
156 | entityHidingEnabled = config.getBoolean("hide-entities-behind-portals");
157 | entityViewingEnabled = config.getBoolean("show-entities-inside-portals");
158 | entityUpdateTicks = clamp(config.getInt("entity-update-ticks"), 1, 3);
159 |
160 | warningMessagesEnabled = config.getBoolean("warning-messages");
161 | debugMessagesEnabled = config.getBoolean("debug-messages");
162 |
163 | worldBorderBlockTypes = new HashMap<>();
164 | worldBorderBlockTypes.put(World.Environment.NORMAL, deserializeWorldBorderBlockType(config, "overworld-border"));
165 | worldBorderBlockTypes.put(World.Environment.NETHER, deserializeWorldBorderBlockType(config, "nether-border"));
166 | worldBorderBlockTypes.put(World.Environment.THE_END, deserializeWorldBorderBlockType(config, "end-border"));
167 | }
168 |
169 | public void loadNetherPortalSettings(FileConfiguration config) {
170 |
171 | ConfigurationSection configSection = config.getConfigurationSection("nether-portal-viewing");
172 |
173 | netherPortalsAreFlippedByDefault = configSection.getBoolean("flip-portals-by-default");
174 | hidePortalBlocks = configSection.getBoolean("hide-portal-blocks");
175 | cancelTeleportWhenLinking = configSection.getBoolean("cancel-teleport-when-linking-portals");
176 | instantTeleportEnabled = configSection.getBoolean("instant-teleport");
177 |
178 | portalViewWhiteList = new HashSet<>();
179 | portalViewBlackList = new HashSet<>();
180 |
181 | allWorldsCanCreatePortalViews = deserializeWorldList(portalViewWhiteList, configSection, "whitelisted-worlds", "nether portal whitelist");
182 | deserializeWorldList(portalViewBlackList, configSection, "blacklisted-worlds", "nether portal blacklist");
183 |
184 | if (allWorldsCanCreatePortalViews) {
185 | MessageUtils.printDebug("Nether portal viewing enabled in all worlds except black listed ones.");
186 | }
187 | }
188 |
189 | public void loadCustomPortalSettings(FileConfiguration config) {
190 |
191 | ConfigurationSection configSection = config.getConfigurationSection("custom-portal-viewing");
192 |
193 | customPortalWhiteList = new HashSet<>();
194 | customPortalBlackList = new HashSet<>();
195 |
196 | allWorldsCanCreateCustomPortals = deserializeWorldList(customPortalWhiteList, configSection, "whitelisted-worlds", "custom portal whitelist");
197 | deserializeWorldList(customPortalBlackList, config, "blacklisted-worlds", "custom portal blacklist");
198 |
199 | if (allWorldsCanCreateCustomPortals) {
200 | MessageUtils.printDebug("Custom portal viewing enabled in all worlds except black listed ones.");
201 | }
202 | }
203 |
204 | private BlockType deserializeWorldBorderBlockType(FileConfiguration config, String configPath) {
205 |
206 | String configValue = config.getString(configPath);
207 | String defaultValue = config.getDefaults().getString(configPath);
208 | BlockType worldBorder;
209 |
210 | try {
211 | worldBorder = BlockType.of(configValue);
212 |
213 | } catch (Exception e) {
214 | plugin.getLogger().log(Level.WARNING, "'" + configValue + "' could not be interpreted as a block type. Using '" + defaultValue + "' instead.");
215 | return BlockType.of(defaultValue);
216 | }
217 |
218 | if (!worldBorder.isOccluding()) {
219 | plugin.getLogger().log(Level.WARNING, "'" + configValue + "' is not an occluding block. Using '" + defaultValue + "' instead.");
220 | return BlockType.of(defaultValue);
221 | }
222 |
223 | return worldBorder;
224 | }
225 |
226 | private boolean deserializeWorldList(Set setToPopulate,
227 | ConfigurationSection config,
228 | String configPath,
229 | String listName) {
230 |
231 | List worldNames = config.getStringList(configPath);
232 |
233 | if (worldNames.contains("*")) {
234 | return true;
235 | }
236 |
237 | worldNames.forEach(worldName -> addWorld(worldName, setToPopulate, listName));
238 | return false;
239 | }
240 |
241 | private void addWorld(String worldName, Collection worldCollection, String listName) {
242 |
243 | World world = Bukkit.getWorld(worldName);
244 |
245 | if (world == null) {
246 | plugin.getLogger().log(Level.WARNING, "Could not find world '" + worldName + "' from " + listName);
247 |
248 | } else {
249 | worldCollection.add(world.getUID());
250 | MessageUtils.printDebug("Added '" + worldName + "' to " + listName);
251 | }
252 | }
253 |
254 | private int clamp(int value, int min, int max) {
255 | return Math.max(min, Math.min(value, max));
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/blockcache/BlockCache.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.blockcache;
2 |
3 | import me.gorgeousone.netherview.geometry.BlockVec;
4 | import me.gorgeousone.netherview.portal.Portal;
5 | import me.gorgeousone.netherview.utils.FacingUtils;
6 | import me.gorgeousone.netherview.wrapper.WrappedBoundingBox;
7 | import me.gorgeousone.netherview.wrapper.blocktype.BlockType;
8 | import org.bukkit.Chunk;
9 | import org.bukkit.World;
10 | import org.bukkit.block.Block;
11 | import org.bukkit.entity.Entity;
12 | import org.bukkit.util.Vector;
13 |
14 | import java.util.HashMap;
15 | import java.util.HashSet;
16 | import java.util.Map;
17 | import java.util.Set;
18 |
19 | /**
20 | * One big array of BlockTypes used to store information about all blocks in a cuboid area around a portal.
21 | * For each of the 2 sides of a portal there is a separate BlockCache.
22 | */
23 | public class BlockCache {
24 |
25 | private final Portal portal;
26 | private final Set chunks;
27 |
28 | private final BlockType[][][] blockCopies;
29 | private final BlockVec min;
30 | private final BlockVec max;
31 |
32 | private final BlockVec facing;
33 | private final BlockType borderType;
34 |
35 | public BlockCache(Portal portal,
36 | BlockVec offset,
37 | BlockVec size,
38 | BlockVec facing,
39 | BlockType borderType) {
40 |
41 | this.portal = portal;
42 | this.chunks = new HashSet<>();
43 |
44 | this.blockCopies = new BlockType[size.getX()][size.getY()][size.getZ()];
45 | this.min = offset.clone();
46 | this.max = offset.clone().add(size());
47 |
48 | this.facing = facing;
49 | this.borderType = borderType;
50 |
51 | for (int chunkX = min.getX() >> 4; chunkX <= max.getX() >> 4; chunkX++) {
52 | for (int chunkZ = min.getZ() >> 4; chunkZ <= max.getZ() >> 4; chunkZ++) {
53 |
54 | chunks.add(portal.getWorld().getChunkAt(chunkX, chunkZ));
55 | }
56 | }
57 | }
58 |
59 | private BlockVec size() {
60 | return new BlockVec(blockCopies.length, blockCopies[0].length, blockCopies[0][0].length);
61 | }
62 |
63 | public Portal getPortal() {
64 | return portal;
65 | }
66 |
67 | public World getWorld() {
68 | return portal.getWorld();
69 | }
70 |
71 | public Set getChunks() {
72 | return chunks;
73 | }
74 |
75 | public BlockVec getMin() {
76 | return min.clone();
77 | }
78 |
79 | public BlockVec getMax() {
80 | return max.clone();
81 | }
82 |
83 | public BlockVec getFacing() {
84 | return facing.clone();
85 | }
86 |
87 | public boolean contains(BlockVec loc) {
88 | return contains(loc.getX(), loc.getY(), loc.getZ());
89 | }
90 |
91 | public boolean contains(Vector loc) {
92 | return contains(loc.getX(), loc.getY(), loc.getZ());
93 | }
94 |
95 | public boolean contains(double x, double y, double z) {
96 | return x >= min.getX() && x < max.getX() &&
97 | y >= min.getY() && y < max.getY() &&
98 | z >= min.getZ() && z < max.getZ();
99 | }
100 |
101 | public boolean isBorder(BlockVec loc) {
102 | return isBorder(loc.getX(), loc.getY(), loc.getZ());
103 | }
104 |
105 | public BlockType getBorderBlockType() {
106 | return borderType.clone();
107 | }
108 |
109 | /**
110 | * Returns true if the block is at any position bordering the cuboid except the side facing the portal.
111 | */
112 | public boolean isBorder(int x, int y, int z) {
113 |
114 | if (y == min.getY() || y == max.getY() - 1) {
115 | return true;
116 | }
117 |
118 | int minX = min.getX();
119 | int minZ = min.getZ();
120 | int maxX = max.getX() - 1;
121 | int maxZ = max.getZ() - 1;
122 |
123 | if (facing.getZ() != 0) {
124 | if (x == minX || x == maxX) {
125 | return true;
126 | }
127 | } else if (z == minZ || z == maxZ) {
128 | return true;
129 | }
130 |
131 | if (facing.getX() == 1) {
132 | return x == maxX;
133 | }
134 | if (facing.getX() == -1) {
135 | return x == minX;
136 | }
137 | if (facing.getZ() == 1) {
138 | return z == maxZ;
139 | } else {
140 | return z == minZ;
141 | }
142 | }
143 |
144 | public BlockType getBlockTypeAt(BlockVec blockPos) {
145 | return getBlockTypeAt(blockPos.getX(),
146 | blockPos.getY(),
147 | blockPos.getZ());
148 | }
149 |
150 | public BlockType getBlockTypeAt(int x, int y, int z) {
151 |
152 | if (!contains(x, y, z)) {
153 | return null;
154 | }
155 |
156 | return blockCopies
157 | [x - min.getX()]
158 | [y - min.getY()]
159 | [z - min.getZ()];
160 | }
161 |
162 | public void setBlockTypeAt(BlockVec blockPos, BlockType blockType) {
163 | setBlockTypeAt(
164 | blockPos.getX(),
165 | blockPos.getY(),
166 | blockPos.getZ(),
167 | blockType);
168 | }
169 |
170 | public void setBlockTypeAt(int x, int y, int z, BlockType blockType) {
171 | blockCopies
172 | [x - min.getX()]
173 | [y - min.getY()]
174 | [z - min.getZ()] = blockType;
175 | }
176 |
177 | public void removeBlockDataAt(BlockVec blockPos) {
178 | blockCopies
179 | [blockPos.getX() - min.getX()]
180 | [blockPos.getY() - min.getY()]
181 | [blockPos.getZ() - min.getZ()] = null;
182 | }
183 |
184 | /**
185 | * Returns true if the block copy at the given position is listed as visible (when it's not null)
186 | */
187 | public boolean isBlockListedVisible(BlockVec blockPos) {
188 | return getBlockTypeAt(blockPos) != null;
189 | }
190 |
191 | /**
192 | * Returns true if the block copy at the given position is visible by checking the surrounding blocks in the world for transparent ones
193 | */
194 | public boolean isBlockNowVisible(BlockVec blockPos) {
195 |
196 | for (BlockVec facing : FacingUtils.getAxesBlockVecs()) {
197 |
198 | BlockVec touchingBlockPos = blockPos.clone().add(facing);
199 |
200 | Block touchingBlock = getWorld().getBlockAt(
201 | touchingBlockPos.getX(),
202 | touchingBlockPos.getY(),
203 | touchingBlockPos.getZ());
204 |
205 |
206 | if (!touchingBlock.getType().isOccluding()) {
207 | return true;
208 | }
209 | }
210 |
211 | //TODO check if block is directly in front of the portal.
212 | return false;
213 | }
214 |
215 | private final Map> unloadedChunks = new HashMap<>();
216 |
217 | /**
218 | * Returns a set of all entities that are intersecting this BlockCache
219 | */
220 | public Set getEntities() {
221 |
222 | Set containedEntities = new HashSet<>();
223 |
224 | for (Chunk chunk : chunks) {
225 |
226 | if (!getWorld().isChunkLoaded(chunk)) {
227 |
228 | unloadedChunks.computeIfAbsent(chunk, set -> addContainedEntities(chunk, new HashSet<>()));
229 | containedEntities.addAll(unloadedChunks.get(chunk));
230 |
231 | } else {
232 |
233 | addContainedEntities(chunk, containedEntities);
234 | unloadedChunks.remove(chunk);
235 | }
236 | }
237 |
238 | return containedEntities;
239 | }
240 |
241 | private Set addContainedEntities(Chunk chunk, Set setToAddTo) {
242 |
243 | boolean wasChunkLoaded = getWorld().isChunkLoaded(chunk);
244 | Entity[] chunkEntities = chunk.getEntities();
245 |
246 | for (int i = 0; i < chunkEntities.length; i++) {
247 |
248 | Entity entity = chunkEntities[i];
249 |
250 | if (WrappedBoundingBox.of(entity).intersectsBlockCache(this)) {
251 | setToAddTo.add(entity);
252 | }
253 | }
254 |
255 | if (!wasChunkLoaded) {
256 | getWorld().unloadChunk(chunk);
257 | }
258 |
259 | return setToAddTo;
260 | }
261 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/blockcache/ProjectionCache.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.blockcache;
2 |
3 | import me.gorgeousone.netherview.geometry.BlockVec;
4 | import me.gorgeousone.netherview.portal.Portal;
5 | import me.gorgeousone.netherview.wrapper.Axis;
6 | import me.gorgeousone.netherview.wrapper.blocktype.BlockType;
7 |
8 | /**
9 | * The equivalent to a BlockCache used to store information about all blocks that will be displayed in the animation of a portal.
10 | * For each of the 2 sides of a portal there will be a separate ProjectionCache.
11 | */
12 | public class ProjectionCache extends BlockCache {
13 |
14 | private final BlockCache sourceCache;
15 | private final Transform linkTransform;
16 | private final int cacheLength;
17 |
18 | public ProjectionCache(Portal portal,
19 | BlockVec offset,
20 | BlockVec size,
21 | BlockVec facing,
22 | BlockType borderType,
23 | BlockCache sourceCache,
24 | Transform linkTransform) {
25 |
26 | super(portal, offset, size, facing, borderType);
27 |
28 | this.sourceCache = sourceCache;
29 | this.linkTransform = linkTransform;
30 | this.cacheLength = portal.getAxis() == Axis.X ? size.getZ() : size.getX();
31 | }
32 |
33 | public BlockCache getSourceCache() {
34 | return sourceCache;
35 | }
36 |
37 | public Transform getLinkTransform() {
38 | return linkTransform.clone();
39 | }
40 |
41 | /**
42 | * Returns the length of the projection cache measured from portal to back wall.
43 | * The value is important for the length of view frustums.
44 | */
45 | public int getCacheLength() {
46 | return cacheLength;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/blockcache/Transform.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.blockcache;
2 |
3 | import me.gorgeousone.netherview.geometry.BlockVec;
4 | import org.bukkit.Location;
5 | import org.bukkit.util.Vector;
6 |
7 | /**
8 | * A class holding the relative translation and rotation between two portals and applying it to BlockVecs.
9 | */
10 | public class Transform {
11 |
12 | private Vector translation;
13 | private Vector rotCenter;
14 | private final int[][] rotYMatrix;
15 |
16 | public Transform() {
17 |
18 | translation = new Vector();
19 | rotCenter = new Vector();
20 | rotYMatrix = new int[][]{{1, 0}, {0, 1}};
21 | }
22 |
23 | protected Transform(BlockVec translation, BlockVec rotCenter, int[][] rotationY) {
24 |
25 | this.translation = translation.toVector();
26 | this.rotCenter = rotCenter.toVector().add(new Vector(0.5, 0, 0.5));
27 | this.rotYMatrix = rotationY;
28 | }
29 |
30 | protected Transform(Vector translation, Vector rotCenter, int[][] rotationY) {
31 |
32 | this.translation = translation.clone();
33 | this.rotCenter = rotCenter.clone();
34 | this.rotYMatrix = rotationY;
35 | }
36 |
37 | public Vector getTranslation() {
38 | return translation.clone();
39 | }
40 |
41 | public Transform setTranslation(Vector pos) {
42 | this.translation = pos.clone();
43 | return this;
44 | }
45 |
46 | public Transform setTranslation(BlockVec pos) {
47 | this.translation = pos.toVector();
48 | return this;
49 | }
50 |
51 | public Transform translate(double dx, double dy, double dz) {
52 | this.translation.add(new Vector(dx, dy, dz));
53 | return this;
54 | }
55 |
56 | public Transform setRotCenter(BlockVec rotCenter) {
57 | this.rotCenter = rotCenter.toVector().add(new Vector(0.5, 0, 0.5));
58 | return this;
59 | }
60 |
61 | public Transform translateRotCenter(double dx, double dy, double dz) {
62 | this.rotCenter.add(new Vector(dx, dy, dz));
63 | return this;
64 | }
65 |
66 | public boolean isRotY90DegRight() {
67 | return rotYMatrix[0][1] == -1;
68 | }
69 |
70 | public boolean isRotY90DegLeft() {
71 | return rotYMatrix[0][1] == 1;
72 | }
73 |
74 | public boolean isRotY180Deg() {
75 | return rotYMatrix[0][0] == -1;
76 | }
77 |
78 | public boolean isRotY0Deg() {
79 | return rotYMatrix[0][0] == 1;
80 | }
81 |
82 | public Transform setRotY90DegRight() {
83 |
84 | rotYMatrix[0][0] = 0;
85 | rotYMatrix[0][1] = -1;
86 | rotYMatrix[1][0] = 1;
87 | rotYMatrix[1][1] = 0;
88 | return this;
89 | }
90 |
91 | public Transform setRotY90DegLeft() {
92 |
93 | rotYMatrix[0][0] = 0;
94 | rotYMatrix[0][1] = 1;
95 | rotYMatrix[1][0] = -1;
96 | rotYMatrix[1][1] = 0;
97 | return this;
98 | }
99 |
100 | public Transform setRotY180Deg() {
101 |
102 | rotYMatrix[0][0] = -1;
103 | rotYMatrix[0][1] = 0;
104 | rotYMatrix[1][0] = 0;
105 | rotYMatrix[1][1] = -1;
106 | return this;
107 | }
108 |
109 | public BlockVec transformVec(BlockVec blockVec) {
110 |
111 | BlockVec blockRotCenter = new BlockVec(rotCenter);
112 | blockVec.subtract(blockRotCenter);
113 | rotateVec(blockVec);
114 | blockVec.add(blockRotCenter);
115 |
116 | return blockVec.add(new BlockVec(translation));
117 | }
118 |
119 | public Vector transformVec(Vector vec) {
120 |
121 | Vector vecRotCenter = rotCenter;
122 | vec.subtract(vecRotCenter);
123 | rotateVec(vec);
124 | return vec.add(vecRotCenter).add(translation);
125 | }
126 |
127 | public Location transformLoc(Location loc) {
128 |
129 | if (!isRotY0Deg()) {
130 |
131 | loc.subtract(rotCenter);
132 | rotateLoc(loc);
133 | loc.add(rotCenter);
134 | }
135 |
136 | if (translation.lengthSquared() != 0) {
137 | loc.add(translation);
138 | }
139 |
140 | return loc;
141 | }
142 |
143 | private BlockVec rotateVec(BlockVec relativeVec) {
144 |
145 | int transX = relativeVec.getX();
146 | int transZ = relativeVec.getZ();
147 |
148 | relativeVec.setX(rotYMatrix[0][0] * transX + rotYMatrix[0][1] * transZ);
149 | relativeVec.setZ(rotYMatrix[1][0] * transX + rotYMatrix[1][1] * transZ);
150 | return relativeVec;
151 | }
152 |
153 | public Vector rotateVec(Vector relativeVec) {
154 |
155 | double transX = relativeVec.getX();
156 | double transZ = relativeVec.getZ();
157 |
158 | relativeVec.setX(rotYMatrix[0][0] * transX + rotYMatrix[0][1] * transZ);
159 | relativeVec.setZ(rotYMatrix[1][0] * transX + rotYMatrix[1][1] * transZ);
160 | return relativeVec;
161 | }
162 |
163 | public Location rotateLoc(Location relativeLoc) {
164 |
165 | double transX = relativeLoc.getX();
166 | double transZ = relativeLoc.getZ();
167 |
168 | relativeLoc.setX(rotYMatrix[0][0] * transX + rotYMatrix[0][1] * transZ);
169 | relativeLoc.setZ(rotYMatrix[1][0] * transX + rotYMatrix[1][1] * transZ);
170 |
171 | relativeLoc.setYaw(rotateYaw(relativeLoc.getYaw()));
172 | return relativeLoc;
173 | }
174 |
175 | public float rotateYaw(float yaw) {
176 |
177 | float rotatedYaw = yaw + getQuarterTurns() * 90;
178 | rotatedYaw %= 360;
179 |
180 | return rotatedYaw;
181 | }
182 |
183 | public int getQuarterTurns() {
184 |
185 | if (isRotY90DegLeft()) {
186 | return 3;
187 | } else if (isRotY180Deg()) {
188 | return 2;
189 | } else if (isRotY90DegRight()) {
190 | return 1;
191 | }
192 |
193 | return 0;
194 | }
195 |
196 | public Transform invert() {
197 |
198 | rotCenter.add(translation);
199 | translation.multiply(-1);
200 | invertRotation();
201 |
202 | return this;
203 | }
204 |
205 | public Transform invertRotation() {
206 |
207 | rotYMatrix[0][1] *= -1;
208 | rotYMatrix[1][0] *= -1;
209 | return this;
210 | }
211 |
212 | @Override
213 | public Transform clone() {
214 | return new Transform(translation.clone(), rotCenter.clone(), cloneRotY());
215 | }
216 |
217 | private int[][] cloneRotY() {
218 |
219 | return new int[][]{
220 | {rotYMatrix[0][0], rotYMatrix[0][1]},
221 | {rotYMatrix[1][0], rotYMatrix[1][1]}
222 | };
223 | }
224 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/blockcache/TransformFactory.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.blockcache;
2 |
3 | import me.gorgeousone.netherview.geometry.BlockVec;
4 | import me.gorgeousone.netherview.portal.Portal;
5 | import me.gorgeousone.netherview.wrapper.Axis;
6 |
7 | public class TransformFactory {
8 |
9 | /**
10 | * Calculates a Transform that can translate and rotate the block cache blocks from one portal
11 | * to the related projection cache blocks of another portal.
12 | *
13 | * @param portal that has the projection cache
14 | * @param counterPortal that has the block cache related to it
15 | * @param isPortalFlipped flips the transform rotation by 180 degrees
16 | */
17 | public static Transform calculateBlockLinkTransform(Portal portal, Portal counterPortal, boolean isPortalFlipped) {
18 |
19 | Transform linkTransform = new Transform();
20 | Axis counterPortalAxis = counterPortal.getAxis();
21 |
22 | BlockVec toLoc = portal.getFrame().getMin();
23 | BlockVec fromLoc;
24 |
25 | if (portal.getAxis() == counterPortalAxis) {
26 |
27 | if (isPortalFlipped) {
28 | fromLoc = counterPortal.getMaxBlockAtFloor();
29 | linkTransform.setRotY180Deg();
30 |
31 | } else {
32 | fromLoc = counterPortal.getFrame().getMin();
33 | }
34 |
35 | } else {
36 |
37 | if (counterPortalAxis == Axis.X ^ isPortalFlipped) {
38 | linkTransform.setRotY90DegLeft();
39 | } else {
40 | linkTransform.setRotY90DegRight();
41 | }
42 |
43 | fromLoc = isPortalFlipped ? counterPortal.getFrame().getMin() : counterPortal.getMaxBlockAtFloor();
44 | }
45 |
46 | linkTransform.setRotCenter(fromLoc);
47 | linkTransform.setTranslation(toLoc.subtract(fromLoc));
48 | return linkTransform;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/argument/ArgType.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.argument;
2 |
3 | public enum ArgType {
4 |
5 | INTEGER("integer"),
6 | DECIMAL("number"),
7 | STRING("string"),
8 | BOOLEAN("boolean");
9 |
10 | private final String simpleName;
11 |
12 | ArgType(String simpleName) {
13 | this.simpleName = simpleName;
14 | }
15 |
16 | public String simpleName() {
17 | return simpleName;
18 | }
19 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/argument/ArgValue.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.argument;
2 |
3 | import org.bukkit.ChatColor;
4 |
5 | public class ArgValue {
6 |
7 | private int intVal;
8 | private double decimalVal;
9 | private String stringVal;
10 | private boolean booleanVal;
11 |
12 | public ArgValue(String stringValue) {
13 | this(ArgType.STRING, stringValue);
14 | }
15 |
16 | public ArgValue(ArgType type, String value) {
17 | setValue(value, type);
18 | }
19 |
20 | public String getString() {
21 | return stringVal;
22 | }
23 |
24 | public int getInt() {
25 | return intVal;
26 | }
27 |
28 | public double getDouble() {
29 | return decimalVal;
30 | }
31 |
32 | public boolean getBoolean() {
33 | return booleanVal;
34 | }
35 |
36 | protected void setValue(String value, ArgType type) {
37 |
38 | try {
39 | switch (type) {
40 |
41 | case INTEGER:
42 | intVal = Integer.parseInt(value);
43 |
44 | case DECIMAL:
45 | decimalVal = Double.parseDouble(value);
46 |
47 | case STRING:
48 | stringVal = value;
49 | break;
50 |
51 | case BOOLEAN:
52 | booleanVal = Boolean.parseBoolean(value);
53 | break;
54 | }
55 |
56 | } catch (Exception e) {
57 | throw new IllegalArgumentException(ChatColor.RED + "'" + value + "' is not a " + type.simpleName() + ".");
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/argument/Argument.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.argument;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | public class Argument {
8 |
9 | private final String name;
10 |
11 | private final ArgType type;
12 | private final List tabList;
13 | private ArgValue defValue;
14 |
15 | public Argument(String name, ArgType type) {
16 | this(name, type, new String[]{});
17 | }
18 |
19 | public Argument(String name, ArgType type, String... tabList) {
20 |
21 | this.name = name;
22 | this.type = type;
23 |
24 | this.tabList = new ArrayList<>();
25 |
26 | if (type == ArgType.BOOLEAN) {
27 | this.tabList.add("true");
28 | this.tabList.add("false");
29 | } else {
30 | this.tabList.addAll(Arrays.asList(tabList));
31 | }
32 | }
33 |
34 | public boolean hasDefault() {
35 | return getDefault() != null;
36 | }
37 |
38 | public ArgValue getDefault() {
39 | return defValue;
40 | }
41 |
42 | public String getName() {
43 | return name;
44 | }
45 |
46 | public ArgType getType() {
47 | return type;
48 | }
49 |
50 | public List getTabList() {
51 | return tabList;
52 | }
53 |
54 | public Argument setDefaultTo(String value) {
55 | defValue = new ArgValue(getType(), value);
56 | return this;
57 | }
58 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/command/ArgCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.command;
2 |
3 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
5 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
6 | import org.bukkit.command.CommandSender;
7 | import org.bukkit.entity.Player;
8 |
9 | import java.util.ArrayList;
10 | import java.util.LinkedList;
11 | import java.util.List;
12 |
13 | public abstract class ArgCommand extends BasicCommand {
14 |
15 | private final List arguments;
16 |
17 | protected ArgCommand(String name, String permission, boolean isPlayerRequired) {
18 | this(name, permission, isPlayerRequired, null);
19 | }
20 |
21 | protected ArgCommand(String name, String permission, boolean isPlayerRequired, ParentCommand parent) {
22 |
23 | super(name, permission, isPlayerRequired, parent);
24 | this.arguments = new ArrayList<>();
25 | }
26 |
27 | public List getArgs() {
28 | return arguments;
29 | }
30 |
31 | protected void addArg(Argument arg) {
32 | arguments.add(arg);
33 | }
34 |
35 | @Override
36 | protected void onCommand(CommandSender sender, String[] stringArgs) {
37 |
38 | int argsSize = getArgs().size();
39 | int stringArgsLength = stringArgs.length;
40 |
41 | ArgValue[] values = new ArgValue[Math.max(argsSize, stringArgsLength)];
42 |
43 | try {
44 | if (stringArgsLength >= argsSize) {
45 | createMoreValuesThanOwnArgs(values, stringArgs);
46 | } else {
47 | createMoreValuesThanSenderInput(values, stringArgs);
48 | }
49 |
50 | } catch (ArrayIndexOutOfBoundsException e) {
51 | sendUsage(sender);
52 | return;
53 |
54 | } catch (IllegalArgumentException e) {
55 | sender.sendMessage(e.getMessage());
56 | return;
57 | }
58 |
59 | onCommand(sender, values);
60 | }
61 |
62 | protected abstract void onCommand(CommandSender sender, ArgValue[] arguments);
63 |
64 | @Override
65 | public List getTabList(CommandSender sender, String[] arguments) {
66 |
67 | if (isPlayerRequired() && !(sender instanceof Player)) {
68 | return null;
69 | }
70 |
71 | if (this.arguments.size() < arguments.length) {
72 | return new LinkedList<>();
73 | }
74 |
75 | return this.arguments.get(arguments.length - 1).getTabList();
76 | }
77 |
78 | @Override
79 | public String getUsage() {
80 |
81 | StringBuilder usage = new StringBuilder(super.getUsage());
82 |
83 | for (Argument arg : getArgs()) {
84 | usage.append(" <");
85 | usage.append(arg.getName());
86 | usage.append(">");
87 | }
88 |
89 | return usage.toString();
90 | }
91 |
92 | protected void createMoreValuesThanOwnArgs(ArgValue[] values, String[] stringArgs) {
93 |
94 | for (int i = 0; i < values.length; i++) {
95 |
96 | values[i] = i < getArgs().size() ?
97 | new ArgValue(getArgs().get(i).getType(), stringArgs[i]) :
98 | new ArgValue(ArgType.STRING, stringArgs[i]);
99 | }
100 | }
101 |
102 | protected void createMoreValuesThanSenderInput(ArgValue[] values, String[] stringArgs) {
103 |
104 | for (int i = 0; i < values.length; i++) {
105 | Argument arg = getArgs().get(i);
106 |
107 | if (i < stringArgs.length) {
108 | values[i] = new ArgValue(getArgs().get(i).getType(), stringArgs[i]);
109 | continue;
110 | }
111 |
112 | if (arg.hasDefault()) {
113 | values[i] = arg.getDefault();
114 | } else {
115 | throw new ArrayIndexOutOfBoundsException();
116 | }
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/command/BasicCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.command;
2 |
3 | import org.bukkit.ChatColor;
4 | import org.bukkit.command.CommandSender;
5 | import org.bukkit.entity.Player;
6 |
7 | import java.util.ArrayList;
8 | import java.util.HashSet;
9 | import java.util.List;
10 | import java.util.Set;
11 |
12 | /**
13 | * This is the beginning of a lot of unnecessary code.
14 | * I mean it is kind of useful because I can create child commands, aliases, number inputs and tab lists on the go but
15 | * yeah it feels somehow unnecessary that this "api" is like at least 10 pages extra code.
16 | * But it's more soft coded so it's also cool in a way.
17 | */
18 | public abstract class BasicCommand {
19 |
20 | private final String name;
21 | private final String permission;
22 | private final boolean isPlayerRequired;
23 |
24 | private final Set aliases;
25 | private final ParentCommand parent;
26 |
27 | protected BasicCommand(String name, String permission, boolean isPlayerRequired) {
28 | this(name, permission, isPlayerRequired, null);
29 | }
30 |
31 | protected BasicCommand(String name, String permission, boolean isPlayerRequired, ParentCommand parent) {
32 |
33 | this.name = name;
34 | this.permission = permission;
35 | this.isPlayerRequired = isPlayerRequired;
36 | this.parent = parent;
37 |
38 | aliases = new HashSet<>();
39 | aliases.add(name);
40 | }
41 |
42 | public String getName() {
43 | return name;
44 | }
45 |
46 | public String getPermission() {
47 | return permission;
48 | }
49 |
50 | public boolean isPlayerRequired() {
51 | return isPlayerRequired;
52 | }
53 |
54 | public ParentCommand getParent() {
55 | return parent;
56 | }
57 |
58 | public boolean isChild() {
59 | return parent != null;
60 | }
61 |
62 | public boolean matches(String alias) {
63 | return aliases.contains(alias);
64 | }
65 |
66 | protected void addAlias(String alias) {
67 | aliases.add(alias);
68 | }
69 |
70 | public void execute(CommandSender sender, String[] arguments) {
71 |
72 | if (isPlayerRequired && !(sender instanceof Player)) {
73 | sender.sendMessage(ChatColor.RED + "Only players can execute this command.");
74 | return;
75 | }
76 |
77 | if (permission != null && !sender.hasPermission(getPermission())) {
78 | sender.sendMessage(ChatColor.RED + "You do not have the permission for this command.");
79 | return;
80 | }
81 |
82 | onCommand(sender, arguments);
83 | }
84 |
85 | protected abstract void onCommand(CommandSender sender, String[] arguments);
86 |
87 | public List getTabList(CommandSender sender, String[] arguments) {
88 | return new ArrayList<>();
89 | }
90 |
91 | public String getUsage() {
92 |
93 | if (isChild()) {
94 | return getParent().getParentUsage() + " " + getName();
95 | } else {
96 | return "/" + getName();
97 | }
98 | }
99 |
100 | public void sendUsage(CommandSender sender) {
101 | sender.sendMessage(ChatColor.RED + "Usage: " + getUsage());
102 | }
103 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/command/ParentCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.command;
2 |
3 | import org.bukkit.command.CommandSender;
4 | import org.bukkit.entity.Player;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.List;
9 |
10 | public class ParentCommand extends BasicCommand {
11 |
12 | private final List children;
13 | private final String childrenType;
14 |
15 | public ParentCommand(String name, String permission, boolean isPlayerRequired, String childrenType) {
16 | this(name, permission, isPlayerRequired, childrenType, null);
17 | }
18 |
19 | public ParentCommand(String name,
20 | String permission,
21 | boolean isPlayerRequired,
22 | String childrenType,
23 | ParentCommand parent) {
24 | super(name, permission, isPlayerRequired, parent);
25 |
26 | this.childrenType = "<" + childrenType + ">";
27 | this.children = new ArrayList<>();
28 | }
29 |
30 | public void addChild(BasicCommand child) {
31 | children.add(child);
32 | }
33 |
34 | @Override
35 | public void onCommand(CommandSender sender, String[] arguments) {
36 |
37 | if (arguments.length == 0) {
38 | sendUsage(sender);
39 | return;
40 | }
41 |
42 | for (BasicCommand child : getChildren()) {
43 |
44 | if (child.matches(arguments[0])) {
45 | child.execute(sender, Arrays.copyOfRange(arguments, 1, arguments.length));
46 | return;
47 | }
48 | }
49 |
50 | sendUsage(sender);
51 | }
52 |
53 | public List getChildren() {
54 | return children;
55 | }
56 |
57 | @Override
58 | public List getTabList(CommandSender sender, String[] arguments) {
59 |
60 | List tabList = new ArrayList<>();
61 |
62 | //create a tab list of the children commands
63 | if (arguments.length == 1) {
64 |
65 | for (BasicCommand child : getChildren()) {
66 |
67 | if (child.isPlayerRequired() && !(sender instanceof Player)) {
68 | continue;
69 | }
70 |
71 | String subPermission = child.getPermission();
72 |
73 | if (subPermission == null || sender.hasPermission(subPermission)) {
74 | tabList.add(child.getName());
75 | }
76 | }
77 |
78 | return tabList;
79 | }
80 |
81 | //forwards the task of creating a tab list to a child commands
82 | for (BasicCommand child : getChildren()) {
83 |
84 | if (!child.matches(arguments[0])) {
85 | continue;
86 | }
87 |
88 | if (child.isPlayerRequired() && !(sender instanceof Player)) {
89 | continue;
90 | }
91 |
92 | String subPermission = getPermission();
93 |
94 | if (subPermission == null || sender.hasPermission(subPermission)) {
95 | return child.getTabList(sender, Arrays.copyOfRange(arguments, 1, arguments.length));
96 | }
97 | }
98 |
99 | return tabList;
100 | }
101 |
102 | @Override
103 | public String getUsage() {
104 | return super.getUsage() + " " + childrenType;
105 | }
106 |
107 | public String getParentUsage() {
108 | return super.getUsage();
109 | }
110 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/handlers/CommandCompleter.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.handlers;
2 |
3 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
4 | import org.bukkit.command.Command;
5 | import org.bukkit.command.CommandSender;
6 | import org.bukkit.command.TabCompleter;
7 | import org.bukkit.entity.Player;
8 |
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | public class CommandCompleter implements TabCompleter {
13 |
14 | private final CommandHandler cmdHandler;
15 |
16 | public CommandCompleter(CommandHandler cmdHandler) {
17 | this.cmdHandler = cmdHandler;
18 | }
19 |
20 | @Override
21 | public List onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
22 |
23 | for (BasicCommand command : cmdHandler.getCommands()) {
24 |
25 | if (command.matches(cmd.getName())) {
26 |
27 | List tabList = new LinkedList<>();
28 | String permission = cmd.getPermission();
29 |
30 | if (command.isPlayerRequired() && !(sender instanceof Player)) {
31 | return tabList;
32 | }
33 |
34 | if (permission != null && !sender.hasPermission(permission)) {
35 | return tabList;
36 | }
37 |
38 | for (String tab : command.getTabList(sender, args)) {
39 |
40 | if (tab.startsWith(args[args.length - 1])) {
41 | tabList.add(tab);
42 | }
43 | }
44 |
45 | return tabList;
46 | }
47 | }
48 |
49 | return null;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/cmdframework/handlers/CommandHandler.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.cmdframework.handlers;
2 |
3 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
4 | import org.bukkit.command.Command;
5 | import org.bukkit.command.CommandExecutor;
6 | import org.bukkit.command.CommandSender;
7 | import org.bukkit.plugin.java.JavaPlugin;
8 |
9 | import java.util.HashSet;
10 | import java.util.Set;
11 |
12 | public class CommandHandler implements CommandExecutor {
13 |
14 | private final JavaPlugin plugin;
15 | private final Set commands;
16 | private final CommandCompleter cmdCompleter;
17 |
18 | public CommandHandler(JavaPlugin plugin) {
19 | this.plugin = plugin;
20 | this.commands = new HashSet<>();
21 | this.cmdCompleter = new CommandCompleter(this);
22 | }
23 |
24 | public void registerCommand(BasicCommand command) {
25 | commands.add(command);
26 | plugin.getCommand(command.getName()).setExecutor(this);
27 | plugin.getCommand(command.getName()).setTabCompleter(cmdCompleter);
28 | }
29 |
30 | public Set getCommands() {
31 | return commands;
32 | }
33 |
34 | @Override
35 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
36 |
37 | String cmdName = cmd.getName();
38 |
39 | for (BasicCommand command : commands) {
40 |
41 | if (command.matches(cmdName)) {
42 | command.execute(sender, args);
43 | return true;
44 | }
45 | }
46 | return false;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/FlipPortalCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
6 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
7 | import me.gorgeousone.netherview.handlers.PortalHandler;
8 | import me.gorgeousone.netherview.handlers.ViewHandler;
9 | import me.gorgeousone.netherview.message.Message;
10 | import me.gorgeousone.netherview.message.MessageUtils;
11 | import me.gorgeousone.netherview.portal.Portal;
12 | import org.bukkit.World;
13 | import org.bukkit.command.CommandSender;
14 | import org.bukkit.entity.Player;
15 |
16 | public class FlipPortalCommand extends BasicCommand {
17 |
18 | private final ConfigSettings configSettings;
19 | private final PortalHandler portalHandler;
20 | private final ViewHandler viewHandler;
21 |
22 | public FlipPortalCommand(ParentCommand parent,
23 | ConfigSettings configSettings,
24 | PortalHandler portalHandler,
25 | ViewHandler viewHandler) {
26 |
27 | super("flipportal", NetherViewPlugin.PORTAL_FLIP_PERM, true, parent);
28 |
29 | this.configSettings = configSettings;
30 | this.portalHandler = portalHandler;
31 | this.viewHandler = viewHandler;
32 | }
33 |
34 | @Override
35 | protected void onCommand(CommandSender sender, String[] arguments) {
36 |
37 | Player player = (Player) sender;
38 | World world = player.getWorld();
39 |
40 | if (!configSettings.canCreatePortalViews(world)) {
41 | MessageUtils.sendInfo(player, Message.WORLD_NOT_WHITE_LISTED, player.getWorld().getName());
42 | return;
43 | }
44 |
45 | if (!viewHandler.hasViewSession(player)) {
46 | MessageUtils.sendInfo(player, Message.NO_PORTAL_FOUND_NEARBY);
47 | return;
48 | }
49 |
50 | Portal viewedPortal = viewHandler.getViewSession(player).getViewedPortal();
51 |
52 | viewHandler.removePortal(viewedPortal);
53 | viewedPortal.flipView();
54 | portalHandler.loadProjectionCachesOf(viewedPortal);
55 | viewHandler.displayClosestPortalTo(player, player.getEyeLocation());
56 |
57 | MessageUtils.sendInfo(player, Message.FLIPPED_PORTAL, viewedPortal.toString());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/ListPortalsCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
6 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
7 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
8 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
9 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
10 | import me.gorgeousone.netherview.handlers.PortalHandler;
11 | import me.gorgeousone.netherview.message.Message;
12 | import me.gorgeousone.netherview.message.MessageUtils;
13 | import me.gorgeousone.netherview.portal.Portal;
14 | import org.bukkit.Bukkit;
15 | import org.bukkit.ChatColor;
16 | import org.bukkit.World;
17 | import org.bukkit.command.CommandSender;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 | import java.util.Set;
22 |
23 | public class ListPortalsCommand extends ArgCommand {
24 |
25 | private final ConfigSettings configSettings;
26 | private final PortalHandler portalHandler;
27 |
28 | public ListPortalsCommand(ParentCommand parent, ConfigSettings configSettings, PortalHandler portalHandler) {
29 |
30 | super("listportals", NetherViewPlugin.INFO_PERM, false, parent);
31 | addArg(new Argument("world", ArgType.STRING));
32 |
33 | this.configSettings = configSettings;
34 | this.portalHandler = portalHandler;
35 | }
36 |
37 | @Override
38 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
39 |
40 | String worldName = arguments[0].getString();
41 | World world = Bukkit.getWorld(worldName);
42 |
43 | if (world == null) {
44 | MessageUtils.sendInfo(sender, Message.NO_WORLD_FOUND, worldName);
45 | return;
46 | }
47 |
48 | if (!configSettings.canCreatePortalViews(world)) {
49 | MessageUtils.sendInfo(sender, Message.WORLD_NOT_WHITE_LISTED, worldName);
50 | return;
51 | }
52 |
53 | if (!portalHandler.hasPortals(world)) {
54 | MessageUtils.sendInfo(sender, Message.NO_PORTALS_FOUND, worldName);
55 | return;
56 | }
57 |
58 | Set portalSet = portalHandler.getPortals(world);
59 | StringBuilder portals = new StringBuilder();
60 |
61 | for (Portal portal : portalSet) {
62 | portals.append("\\n").append(ChatColor.GRAY + "- ").append(ChatColor.RESET).append(portal.toString());
63 | }
64 |
65 | MessageUtils.sendInfo(sender, Message.WORLD_INFO, String.valueOf(portalSet.size()), worldName, portals.toString());
66 | }
67 |
68 | @Override
69 | public List getTabList(CommandSender sender, String[] arguments) {
70 |
71 | List worldNames = new ArrayList<>();
72 |
73 | for (World world : Bukkit.getWorlds()) {
74 |
75 | if (world != null) {
76 | worldNames.add(world.getName());
77 | }
78 | }
79 |
80 | return worldNames;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/PortalInfoCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
6 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
7 | import me.gorgeousone.netherview.customportal.CustomPortal;
8 | import me.gorgeousone.netherview.handlers.PortalHandler;
9 | import me.gorgeousone.netherview.message.Message;
10 | import me.gorgeousone.netherview.message.MessageUtils;
11 | import me.gorgeousone.netherview.portal.Portal;
12 | import org.bukkit.ChatColor;
13 | import org.bukkit.World;
14 | import org.bukkit.command.CommandSender;
15 | import org.bukkit.entity.Player;
16 |
17 | import java.util.Set;
18 |
19 | public class PortalInfoCommand extends BasicCommand {
20 |
21 | private final ConfigSettings configSettings;
22 | private final PortalHandler portalHandler;
23 |
24 | public PortalInfoCommand(ParentCommand parent, ConfigSettings configSettings, PortalHandler portalHandler) {
25 |
26 | super("portalinfo", NetherViewPlugin.INFO_PERM, true, parent);
27 |
28 | this.configSettings = configSettings;
29 | this.portalHandler = portalHandler;
30 | }
31 |
32 | @Override
33 | protected void onCommand(CommandSender sender, String[] arguments) {
34 |
35 | Player player = (Player) sender;
36 | World world = player.getWorld();
37 |
38 | if (!configSettings.canCreatePortalViews(world)) {
39 | MessageUtils.sendInfo(player, Message.WORLD_NOT_WHITE_LISTED, player.getWorld().getName());
40 | return;
41 | }
42 |
43 | Portal portal = portalHandler.getClosestPortal(player.getLocation(), false);
44 |
45 | if (portal == null) {
46 | MessageUtils.sendInfo(player, Message.NO_PORTALS_FOUND, player.getWorld().getName());
47 | return;
48 | }
49 |
50 | StringBuilder infoText = new StringBuilder();
51 |
52 | // &7 is flipped: &r%is-flipped%
53 | // &7 is linked to: &r%counter-portal%
54 | // &7 portals linked to portal: &r%linked-portals%
55 |
56 | if (portal instanceof CustomPortal) {
57 | infoText.append("\\n" + ChatColor.GRAY + "name: " + ChatColor.RESET + ((CustomPortal) portal).getName());
58 | }
59 |
60 | infoText.append("\\n" + ChatColor.GRAY + "is flipped: " + ChatColor.RESET + portal.isViewFlipped());
61 | infoText.append("\\n" + ChatColor.GRAY + "is linked: " + ChatColor.RESET + (portal.isLinked() ? portal.getCounterPortal().toString() : "-no portal-"));
62 | infoText.append("\\n" + ChatColor.GRAY + "is linked: " + ChatColor.RESET);
63 |
64 | Set connectedPortals = portalHandler.getPortalsLinkedTo(portal);
65 |
66 | if (connectedPortals.isEmpty()) {
67 | infoText.append("-no portal-");
68 |
69 | } else {
70 |
71 | for (Portal connectedPortal : connectedPortals) {
72 | infoText.append("\\n" + ChatColor.GRAY + " - " + ChatColor.RESET + connectedPortal.toString());
73 | }
74 | }
75 |
76 | MessageUtils.sendInfo(player, Message.PORTAL_INFO, portal.toString(), infoText.toString());
77 | }
78 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/ReloadCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
5 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
6 | import org.bukkit.command.CommandSender;
7 |
8 | public class ReloadCommand extends BasicCommand {
9 |
10 | private final NetherViewPlugin main;
11 |
12 | public ReloadCommand(ParentCommand parent, NetherViewPlugin main) {
13 |
14 | super("reload", NetherViewPlugin.CONFIG_PERM, false, parent);
15 | addAlias("rl");
16 |
17 | this.main = main;
18 | }
19 |
20 | @Override
21 | protected void onCommand(CommandSender sender, String[] arguments) {
22 |
23 | main.reload();
24 | sender.sendMessage(NetherViewPlugin.CHAT_PREFIX + " Reloaded config settings.");
25 | }
26 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/ToggleDebugCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import org.bukkit.command.CommandSender;
10 |
11 | public class ToggleDebugCommand extends ArgCommand {
12 |
13 | private final NetherViewPlugin main;
14 |
15 | public ToggleDebugCommand(ParentCommand parent, NetherViewPlugin main) {
16 |
17 | super("debugmessages", NetherViewPlugin.CONFIG_PERM, false, parent);
18 | addArg(new Argument("true/false", ArgType.BOOLEAN));
19 |
20 | this.main = main;
21 | }
22 |
23 | @Override
24 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
25 |
26 | boolean newState = arguments[0].getBoolean();
27 | boolean stateChanged = main.setDebugMessagesEnabled(newState);
28 |
29 | if (stateChanged) {
30 | sender.sendMessage(NetherViewPlugin.CHAT_PREFIX + (newState ? " Enabled" : " Disabled") + " debug messages.");
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/TogglePortalViewCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
5 | import me.gorgeousone.netherview.handlers.ViewHandler;
6 | import me.gorgeousone.netherview.message.Message;
7 | import me.gorgeousone.netherview.message.MessageUtils;
8 | import org.bukkit.command.CommandSender;
9 | import org.bukkit.entity.Player;
10 |
11 | public class TogglePortalViewCommand extends BasicCommand {
12 |
13 | private final ViewHandler viewHandler;
14 |
15 | public TogglePortalViewCommand(ViewHandler viewHandler) {
16 |
17 | super("toggleportalview", NetherViewPlugin.VIEW_PERM, true);
18 | this.viewHandler = viewHandler;
19 | }
20 |
21 | @Override
22 | protected void onCommand(CommandSender sender, String[] arguments) {
23 |
24 | Player player = (Player) sender;
25 |
26 | boolean wantsToSeePortalViews = !viewHandler.hasPortalViewEnabled(player);
27 | viewHandler.setPortalViewEnabled(player, wantsToSeePortalViews);
28 |
29 | MessageUtils.sendInfo(player, wantsToSeePortalViews ? Message.PORTAL_VIEWING_ON : Message.PORTAL_VIEWING_OFF);
30 | }
31 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/commmands/ToggleWarningsCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.commmands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import org.bukkit.command.CommandSender;
10 |
11 | public class ToggleWarningsCommand extends ArgCommand {
12 |
13 | private final NetherViewPlugin main;
14 |
15 | public ToggleWarningsCommand(ParentCommand parent, NetherViewPlugin main) {
16 |
17 | super("warningmessages", NetherViewPlugin.CONFIG_PERM, false, parent);
18 | addArg(new Argument("true/false", ArgType.BOOLEAN));
19 |
20 | this.main = main;
21 | }
22 |
23 | @Override
24 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
25 |
26 | boolean newState = arguments[0].getBoolean();
27 | boolean stateChanged = main.setWarningMessagesEnabled(newState);
28 |
29 | if (stateChanged) {
30 | sender.sendMessage(NetherViewPlugin.CHAT_PREFIX + (newState ? " Enabled" : " Disabled") + " warning messages.");
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/CustomPortal.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import me.gorgeousone.netherview.geometry.AxisAlignedRect;
4 | import me.gorgeousone.netherview.geometry.Cuboid;
5 | import me.gorgeousone.netherview.portal.Portal;
6 | import org.bukkit.World;
7 |
8 | import java.util.HashSet;
9 |
10 | public class CustomPortal extends Portal {
11 |
12 | private String name;
13 |
14 | public CustomPortal(World world,
15 | AxisAlignedRect portalRect,
16 | Cuboid frameShape,
17 | Cuboid innerShape) {
18 | super(world, portalRect, frameShape, innerShape, new HashSet<>());
19 | }
20 |
21 | public String getName() {
22 | return name;
23 | }
24 |
25 | public void setName(String name) {
26 | this.name = name;
27 | }
28 |
29 | @Override
30 | public void setLinkedTo(Portal counterPortal) {
31 |
32 | if (counterPortal instanceof CustomPortal) {
33 | super.setLinkedTo(counterPortal);
34 | } else {
35 | throw new IllegalArgumentException("Cannot link custom portal to not custom portal");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/CustomPortalCreator.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import me.gorgeousone.netherview.geometry.AxisAlignedRect;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.geometry.Cuboid;
6 | import me.gorgeousone.netherview.message.Message;
7 | import me.gorgeousone.netherview.message.MessageException;
8 | import me.gorgeousone.netherview.wrapper.Axis;
9 | import org.bukkit.World;
10 | import org.bukkit.util.Vector;
11 |
12 | public class CustomPortalCreator {
13 |
14 | public static CustomPortal createPortal(World world, Cuboid portalFrame) throws MessageException {
15 |
16 | int widthX = portalFrame.getWidthX();
17 | int widthZ = portalFrame.getWidthZ();
18 |
19 | if (widthX > 1 && widthZ > 1) {
20 | throw new MessageException(Message.SELECTION_NOT_FLAT);
21 | }
22 |
23 | Axis axis = widthX == 1 ? Axis.Z : Axis.X;
24 | BlockVec frameExtent = new BlockVec(axis.getCrossNormal()).setY(1);
25 |
26 | Cuboid portalInner = portalFrame.clone()
27 | .translateMin(frameExtent)
28 | .translateMax(frameExtent.multiply(-1));
29 |
30 | Vector rectMin = portalInner.getMin().toVector();
31 | Vector rectMax = portalInner.getMax().toVector().subtract(axis.getNormal());
32 | AxisAlignedRect portalRect;
33 |
34 | try {
35 | portalRect = new AxisAlignedRect(axis, rectMin, rectMax);
36 | portalRect.translate(axis.getNormal().multiply(0.5));
37 |
38 | } catch (IllegalArgumentException e) {
39 | throw new MessageException(Message.SELECTION_TOO_SMALL);
40 | }
41 |
42 | if (portalRect.width() < 1 || portalRect.height() < 1) {
43 | throw new MessageException(Message.SELECTION_TOO_SMALL);
44 | } else if (portalRect.width() > 20 || portalRect.height() > 20) {
45 | throw new MessageException(Message.PORTAL_TOO_BIG, "20");
46 | }
47 |
48 | return new CustomPortal(world, portalRect, portalFrame, portalInner);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/CustomPortalHandler.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 |
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import org.bukkit.Location;
6 | import org.bukkit.World;
7 |
8 | import java.util.HashMap;
9 | import java.util.HashSet;
10 | import java.util.Map;
11 | import java.util.Set;
12 | import java.util.UUID;
13 |
14 | public class CustomPortalHandler {
15 |
16 | private final Map customPortals;
17 | private final Map> worldsWithCustomPortals;
18 |
19 | public CustomPortalHandler() {
20 | this.worldsWithCustomPortals = new HashMap<>();
21 | this.customPortals = new HashMap<>();
22 | }
23 |
24 | public void reload() {
25 |
26 | customPortals.clear();
27 | worldsWithCustomPortals.clear();
28 | }
29 |
30 | public CustomPortal getPortal(String portalName) {
31 | return customPortals.get(portalName);
32 | }
33 |
34 | public void addPortal(CustomPortal portal) {
35 |
36 | if (customPortals.containsKey(portal.getName())) {
37 | throw new IllegalArgumentException("Custom portal with this name already exists.");
38 | }
39 |
40 | customPortals.put(portal.getName(), portal);
41 |
42 | UUID worldId = portal.getWorld().getUID();
43 | worldsWithCustomPortals.computeIfAbsent(worldId, set -> new HashSet<>());
44 | worldsWithCustomPortals.get(worldId).add(portal);
45 | }
46 |
47 | public void removePortal(CustomPortal portal) {
48 |
49 | customPortals.remove(portal.getName());
50 | worldsWithCustomPortals.get(portal.getWorld().getUID()).remove(portal);
51 | }
52 |
53 | public Set getPortalNames() {
54 | return customPortals.keySet();
55 | }
56 |
57 | public Set getCustomPortals(World world) {
58 | return worldsWithCustomPortals.getOrDefault(world.getUID(), new HashSet<>());
59 | }
60 |
61 | public CustomPortal getPortalAt(Location location) {
62 |
63 | for (CustomPortal portal : getCustomPortals(location.getWorld())) {
64 |
65 | if (portal.getInner().contains(new BlockVec(location))) {
66 | return portal;
67 | }
68 | }
69 |
70 | return null;
71 | }
72 |
73 | public boolean isValidName(String portalName) {
74 | return portalName.matches("^(?=.{1,32}$)[a-z0-9_-]+");
75 | }
76 |
77 | public boolean isUniqueName(String portalName) {
78 | return !customPortals.containsKey(portalName);
79 | }
80 |
81 | public String createGenericPortalName() {
82 |
83 | for (int i = 1; i <= 10000; ++i) {
84 |
85 | String genericName = "portal" + i;
86 |
87 | if (isUniqueName(genericName)) {
88 | return genericName;
89 | }
90 | }
91 |
92 | return "there is no way you created over 10,000 custom portals";
93 | }
94 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/CustomPortalSerializer.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.geometry.Cuboid;
6 | import me.gorgeousone.netherview.handlers.PortalHandler;
7 | import me.gorgeousone.netherview.message.MessageException;
8 | import me.gorgeousone.netherview.portal.Portal;
9 | import org.bukkit.Bukkit;
10 | import org.bukkit.World;
11 | import org.bukkit.configuration.ConfigurationSection;
12 | import org.bukkit.configuration.file.FileConfiguration;
13 | import org.bukkit.plugin.java.JavaPlugin;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Set;
18 | import java.util.UUID;
19 |
20 | public class CustomPortalSerializer {
21 |
22 | private final JavaPlugin plugin;
23 | private final ConfigSettings configSettings;
24 | private final PortalHandler portalHandler;
25 | private final CustomPortalHandler customPortalHandler;
26 |
27 | public CustomPortalSerializer(JavaPlugin plugin,
28 | ConfigSettings configSettings,
29 | PortalHandler portalHandler,
30 | CustomPortalHandler customPortalHandler) {
31 |
32 | this.configSettings = configSettings;
33 | this.portalHandler = portalHandler;
34 | this.plugin = plugin;
35 | this.customPortalHandler = customPortalHandler;
36 | }
37 |
38 | public void savePortals(FileConfiguration customPortalConfig) {
39 |
40 | customPortalConfig.set("plugin-version", plugin.getDescription().getVersion());
41 | customPortalConfig.set("custom-portals", null);
42 |
43 | ConfigurationSection portalSection = customPortalConfig.createSection("custom-portals");
44 |
45 | for (World world : Bukkit.getWorlds()) {
46 |
47 | if (!portalHandler.hasPortals(world)) {
48 | continue;
49 | }
50 |
51 | Set portalsInWorld = portalHandler.getPortals(world);
52 | String worldId = world.getUID().toString();
53 | ConfigurationSection worldSection = null;
54 |
55 | for (Portal portal : portalsInWorld) {
56 |
57 | if (!(portal instanceof CustomPortal)) {
58 | continue;
59 | }
60 |
61 | if (worldSection == null) {
62 | worldSection = portalSection.createSection(worldId);
63 | }
64 |
65 | CustomPortal customPortal = (CustomPortal) portal;
66 | String portalName = customPortal.getName();
67 | Cuboid portalFrame = customPortal.getFrame();
68 | ConfigurationSection portalData = worldSection.createSection(portalName);
69 |
70 | portalData.set("min", portalFrame.getMin().serialize());
71 | portalData.set("max", portalFrame.getMax().serialize());
72 | portalData.set("is-flipped", portal.isViewFlipped());
73 |
74 | if (customPortal.isLinked()) {
75 | portalData.set("link", ((CustomPortal) customPortal.getCounterPortal()).getName());
76 | }
77 | }
78 | }
79 | }
80 |
81 | public void loadPortals(FileConfiguration customPortalConfig) {
82 |
83 | if (!customPortalConfig.contains("custom-portals")) {
84 | return;
85 | }
86 |
87 | ConfigurationSection portalsSection = customPortalConfig.getConfigurationSection("custom-portals");
88 | Map portalLinks = new HashMap<>();
89 |
90 | for (String worldId : portalsSection.getKeys(false)) {
91 |
92 | World world = Bukkit.getWorld(UUID.fromString(worldId));
93 |
94 | if (world == null) {
95 | plugin.getLogger().warning("Could not find world with ID: '" + worldId + "'. Portals saved for this world will not be loaded.");
96 | continue;
97 | }
98 |
99 | if (configSettings.canCreateCustomPortals(world)) {
100 |
101 | ConfigurationSection worldSection = portalsSection.getConfigurationSection(worldId);
102 |
103 | for (String portalName : worldSection.getKeys(false)) {
104 | deserializeCustomPortal(world, portalName, worldSection.getConfigurationSection(portalName), portalLinks);
105 | }
106 | }
107 | }
108 |
109 | linkCustomPortals(portalLinks);
110 | }
111 |
112 | private void deserializeCustomPortal(World world,
113 | String portalName,
114 | ConfigurationSection portalData,
115 | Map portalLinks) {
116 |
117 | try {
118 | BlockVec portalMin = BlockVec.fromString(portalData.getString("min"));
119 | BlockVec portalMax = BlockVec.fromString(portalData.getString("max"));
120 |
121 | Cuboid portalFrame = new Cuboid(portalMin, portalMax);
122 | CustomPortal portal = CustomPortalCreator.createPortal(world, portalFrame);
123 |
124 | portal.setName(portalName);
125 | portal.setViewFlipped(portalData.getBoolean("is-flipped"));
126 |
127 | customPortalHandler.addPortal(portal);
128 | portalHandler.addPortal(portal);
129 |
130 | if (portalData.contains("link")) {
131 | portalLinks.put(portalName, portalData.getString("link"));
132 | }
133 |
134 | } catch (IllegalArgumentException | IllegalStateException | MessageException e) {
135 | plugin.getLogger().warning("Unable to load custom portal at [" + world.getName() + "," + portalName + "]: " + e.getMessage());
136 | }
137 | }
138 |
139 | private void linkCustomPortals(Map portalLinks) {
140 |
141 | for (Map.Entry entry : portalLinks.entrySet()) {
142 |
143 | String fromName = entry.getKey();
144 | String toName = entry.getValue();
145 |
146 | CustomPortal fromPortal = customPortalHandler.getPortal(entry.getKey());
147 | CustomPortal toPortal = customPortalHandler.getPortal(entry.getValue());
148 |
149 | if (toPortal == null) {
150 | plugin.getLogger().warning("Could not find custom portal with name'" + toName + "' for linking with portal '" + fromName + "'.");
151 | continue;
152 | }
153 |
154 | try {
155 | portalHandler.linkPortalTo(fromPortal, toPortal, null);
156 |
157 | } catch (MessageException e) {
158 | plugin.getLogger().warning("Unable to link custom portal '" + fromName + "' to portal '" + toName + "': " + e.getMessage());
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/PlayerClickListener.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.geometry.BlockVec;
6 | import me.gorgeousone.netherview.message.Message;
7 | import me.gorgeousone.netherview.message.MessageUtils;
8 | import org.bukkit.GameMode;
9 | import org.bukkit.Material;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.event.EventHandler;
12 | import org.bukkit.event.Listener;
13 | import org.bukkit.event.block.Action;
14 | import org.bukkit.event.player.PlayerInteractEvent;
15 | import org.bukkit.inventory.EquipmentSlot;
16 | import org.bukkit.inventory.ItemStack;
17 |
18 | public class PlayerClickListener implements Listener {
19 |
20 | private final PlayerSelectionHandler selectionHandler;
21 | private final ConfigSettings configSettings;
22 |
23 | public PlayerClickListener(PlayerSelectionHandler selectionHandler,
24 | ConfigSettings configSettings) {
25 | this.selectionHandler = selectionHandler;
26 | this.configSettings = configSettings;
27 | }
28 |
29 | @EventHandler
30 | public void onPlayerClick(PlayerInteractEvent event) {
31 |
32 | if (!configSettings.canCreateCustomPortals(event.getPlayer().getWorld())) {
33 | return;
34 | }
35 |
36 | Action action = event.getAction();
37 |
38 | if (action != Action.LEFT_CLICK_BLOCK && action != Action.RIGHT_CLICK_BLOCK || isOffHandClick(event)) {
39 | return;
40 | }
41 |
42 | Player player = event.getPlayer();
43 |
44 | if (player.getGameMode() != GameMode.CREATIVE || !player.hasPermission(NetherViewPlugin.CUSTOM_PORTAL_PERM)) {
45 | return;
46 | }
47 |
48 | ItemStack itemInHand = getHandItem(player);
49 |
50 | if (itemInHand == null || itemInHand.getType() != Material.BLAZE_ROD) {
51 | return;
52 | }
53 |
54 | event.setCancelled(true);
55 | BlockVec clickedPos = new BlockVec(event.getClickedBlock());
56 | PlayerCuboidSelection selection = selectionHandler.getOrCreateCuboidSelection(player);
57 |
58 | if (action == Action.LEFT_CLICK_BLOCK) {
59 |
60 | if (clickedPos.equals(selection.getPos1())) {
61 | return;
62 | }
63 |
64 | selection.setPos1(clickedPos);
65 | MessageUtils.sendInfo(player, Message.SET_FIRST_CUBOID_POSITION, clickedPos.toString());
66 |
67 | } else {
68 |
69 | if (clickedPos.equals(selection.getPos2())) {
70 | return;
71 | }
72 |
73 | selection.setPos2(clickedPos);
74 | MessageUtils.sendInfo(player, Message.SET_SECOND_CUBOID_POSITION, clickedPos.toString());
75 | }
76 | }
77 |
78 | private boolean isOffHandClick(PlayerInteractEvent event) {
79 |
80 | try {
81 | return event.getHand() == EquipmentSlot.OFF_HAND;
82 | } catch (NoSuchMethodError e) {
83 | return false;
84 | }
85 | }
86 |
87 | private ItemStack getHandItem(Player player) {
88 |
89 | try {
90 | return player.getInventory().getItemInMainHand();
91 | } catch (NoSuchMethodError e) {
92 | return player.getItemInHand();
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/PlayerCuboidSelection.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import me.gorgeousone.netherview.geometry.BlockVec;
4 | import me.gorgeousone.netherview.geometry.Cuboid;
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.World;
7 | import org.bukkit.entity.Player;
8 |
9 | import java.util.UUID;
10 |
11 | public class PlayerCuboidSelection {
12 |
13 | private final UUID playerId;
14 | private final World world;
15 |
16 | private BlockVec pos1;
17 | private BlockVec pos2;
18 |
19 | public PlayerCuboidSelection(Player player) {
20 |
21 | this.playerId = player.getUniqueId();
22 | this.world = player.getWorld();
23 | }
24 |
25 | public Player getPlayer() {
26 | return Bukkit.getPlayer(playerId);
27 | }
28 |
29 | public World getWorld() {
30 | return world;
31 | }
32 |
33 | public BlockVec getPos1() {
34 | return pos1;
35 | }
36 |
37 | public void setPos1(BlockVec pos1) {
38 | this.pos1 = pos1;
39 | }
40 |
41 | public BlockVec getPos2() {
42 | return pos2;
43 | }
44 |
45 | public void setPos2(BlockVec pos2) {
46 | this.pos2 = pos2;
47 | }
48 |
49 | public boolean bothPositionsAreSet() {
50 | return pos1 != null && pos2 != null;
51 | }
52 |
53 | public Cuboid getCuboid() {
54 | return new Cuboid(pos1, pos2).translateMax(1, 1, 1);
55 | }
56 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/PlayerSelectionHandler.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal;
2 |
3 | import org.bukkit.entity.Player;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.UUID;
8 |
9 | public class PlayerSelectionHandler {
10 |
11 | private final Map cuboidSelections;
12 |
13 | public PlayerSelectionHandler() {
14 | this.cuboidSelections = new HashMap<>();
15 | }
16 |
17 | public boolean hasCuboidSelection(Player player) {
18 | return cuboidSelections.containsKey(player.getUniqueId());
19 | }
20 |
21 | public PlayerCuboidSelection getSelection(Player player) {
22 | return cuboidSelections.get(player.getUniqueId());
23 | }
24 |
25 | public PlayerCuboidSelection getOrCreateCuboidSelection(Player player) {
26 |
27 | UUID playerId = player.getUniqueId();
28 | PlayerCuboidSelection selection = cuboidSelections.get(playerId);
29 |
30 | if (selection == null || selection.getWorld() != player.getWorld()) {
31 | selection = new PlayerCuboidSelection(player);
32 | cuboidSelections.put(playerId, selection);
33 | }
34 |
35 | return selection;
36 | }
37 |
38 | public void removeSelection(Player player) {
39 | cuboidSelections.remove(player.getUniqueId());
40 | }
41 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/commands/CreatePortalCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal.commands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import me.gorgeousone.netherview.customportal.CustomPortal;
10 | import me.gorgeousone.netherview.customportal.CustomPortalCreator;
11 | import me.gorgeousone.netherview.customportal.CustomPortalHandler;
12 | import me.gorgeousone.netherview.customportal.PlayerCuboidSelection;
13 | import me.gorgeousone.netherview.customportal.PlayerSelectionHandler;
14 | import me.gorgeousone.netherview.handlers.PortalHandler;
15 | import me.gorgeousone.netherview.message.Message;
16 | import me.gorgeousone.netherview.message.MessageException;
17 | import me.gorgeousone.netherview.message.MessageUtils;
18 | import org.bukkit.command.CommandSender;
19 | import org.bukkit.entity.Player;
20 |
21 | import java.util.Locale;
22 |
23 | public class CreatePortalCommand extends ArgCommand {
24 |
25 | private final static String genericNamePlaceHolder = "auto_inc";
26 |
27 | private final PlayerSelectionHandler selectionHandler;
28 | private final PortalHandler portalHandler;
29 | private final CustomPortalHandler customPortalHandler;
30 |
31 | public CreatePortalCommand(ParentCommand parent,
32 | PlayerSelectionHandler selectionHandler,
33 | PortalHandler portalHandler,
34 | CustomPortalHandler customPortalHandler) {
35 |
36 | super("createportal", NetherViewPlugin.CUSTOM_PORTAL_PERM, true, parent);
37 | addArg(new Argument("portal name", ArgType.STRING).setDefaultTo(genericNamePlaceHolder));
38 |
39 | this.selectionHandler = selectionHandler;
40 | this.portalHandler = portalHandler;
41 | this.customPortalHandler = customPortalHandler;
42 | }
43 |
44 | @Override
45 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
46 |
47 | Player player = (Player) sender;
48 |
49 | if (!selectionHandler.hasCuboidSelection(player)) {
50 | MessageUtils.sendInfo(player, Message.SELECTION_INCOMPLETE);
51 | return;
52 | }
53 |
54 | PlayerCuboidSelection selection = selectionHandler.getSelection(player);
55 |
56 | if (!selection.bothPositionsAreSet()) {
57 | MessageUtils.sendInfo(player, Message.SELECTION_INCOMPLETE);
58 | return;
59 | }
60 |
61 | CustomPortal portal;
62 |
63 | try {
64 | portal = CustomPortalCreator.createPortal(player.getWorld(), selection.getCuboid());
65 |
66 | } catch (MessageException e) {
67 | MessageUtils.sendInfo(player, e.getPlayerMessage(), e.getPlaceholderValues());
68 | return;
69 | }
70 |
71 | if (portalHandler.portalIntersectsOtherPortals(portal)) {
72 | MessageUtils.sendInfo(player, Message.PORTALS_INTERSECT);
73 | return;
74 | }
75 |
76 | String portalName = arguments[0].getString().toLowerCase(Locale.ENGLISH);
77 |
78 | if (portalName.equals(genericNamePlaceHolder)) {
79 | portalName = customPortalHandler.createGenericPortalName();
80 |
81 | } else if (!customPortalHandler.isValidName(portalName)) {
82 | MessageUtils.sendInfo(player, Message.PORTAL_NAME_NOT_VALID);
83 | return;
84 |
85 | } else if (!customPortalHandler.isUniqueName(portalName)) {
86 | MessageUtils.sendInfo(player, Message.PORTAL_NAME_NOT_UNIQUE, portalName);
87 | return;
88 | }
89 |
90 | portal.setName(portalName);
91 | portalHandler.addPortal(portal);
92 | customPortalHandler.addPortal(portal);
93 | MessageUtils.sendInfo(player, Message.CREATED_PORTAL, portalName, portal.width() + "x" + portal.height());
94 | }
95 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/commands/DeletePortalCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal.commands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import me.gorgeousone.netherview.customportal.CustomPortal;
10 | import me.gorgeousone.netherview.customportal.CustomPortalHandler;
11 | import me.gorgeousone.netherview.handlers.PortalHandler;
12 | import me.gorgeousone.netherview.message.Message;
13 | import me.gorgeousone.netherview.message.MessageUtils;
14 | import org.bukkit.command.CommandSender;
15 |
16 | import java.util.List;
17 | import java.util.stream.Collectors;
18 |
19 | public class DeletePortalCommand extends ArgCommand {
20 |
21 | private final PortalHandler portalHandler;
22 | private final CustomPortalHandler customPortalHandler;
23 |
24 | public DeletePortalCommand(ParentCommand parent,
25 | PortalHandler portalHandler,
26 | CustomPortalHandler customPortalHandler) {
27 |
28 | super("deleteportal", NetherViewPlugin.CUSTOM_PORTAL_PERM, false, parent);
29 | addArg(new Argument("portal name", ArgType.STRING));
30 |
31 | this.portalHandler = portalHandler;
32 | this.customPortalHandler = customPortalHandler;
33 | }
34 |
35 | @Override
36 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
37 |
38 | String portalName = arguments[0].getString();
39 | CustomPortal portal = customPortalHandler.getPortal(portalName);
40 |
41 | if (portal == null) {
42 | MessageUtils.sendInfo(sender, Message.NO_PORTAL_FOUND_WITH_NAME, portalName);
43 | return;
44 | }
45 |
46 | customPortalHandler.removePortal(portal);
47 | portalHandler.removePortal(portal);
48 | MessageUtils.sendInfo(sender, Message.REMOVED_PORTAL, portalName);
49 | }
50 |
51 | @Override
52 | public List getTabList(CommandSender sender, String[] arguments) {
53 |
54 | if (arguments.length <= 1) {
55 |
56 | return customPortalHandler.getPortalNames().stream().
57 | filter(name -> name.startsWith(arguments[arguments.length - 1])).
58 | collect(Collectors.toList());
59 | }
60 |
61 | return super.getTabList(sender, arguments);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/commands/GetWandCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal.commands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.command.BasicCommand;
5 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
6 | import me.gorgeousone.netherview.message.Message;
7 | import me.gorgeousone.netherview.message.MessageUtils;
8 | import org.bukkit.Material;
9 | import org.bukkit.command.CommandSender;
10 | import org.bukkit.entity.Player;
11 | import org.bukkit.inventory.ItemStack;
12 |
13 | public class GetWandCommand extends BasicCommand {
14 |
15 | public GetWandCommand(ParentCommand parent) {
16 | super("wand", NetherViewPlugin.PORTAL_WAND_PERM, true, parent);
17 | }
18 |
19 | @Override
20 | protected void onCommand(CommandSender sender, String[] arguments) {
21 |
22 | Player player = (Player) sender;
23 | player.getInventory().addItem(new ItemStack(Material.BLAZE_ROD));
24 | MessageUtils.sendInfo(player, Message.WAND_INFO);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/commands/LinkPortalCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal.commands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import me.gorgeousone.netherview.customportal.CustomPortalHandler;
10 | import me.gorgeousone.netherview.handlers.PortalHandler;
11 | import me.gorgeousone.netherview.handlers.ViewHandler;
12 | import me.gorgeousone.netherview.message.Message;
13 | import me.gorgeousone.netherview.message.MessageException;
14 | import me.gorgeousone.netherview.message.MessageUtils;
15 | import me.gorgeousone.netherview.portal.Portal;
16 | import org.bukkit.command.CommandSender;
17 | import org.bukkit.entity.Player;
18 |
19 | import java.util.List;
20 | import java.util.stream.Collectors;
21 |
22 | public class LinkPortalCommand extends ArgCommand {
23 |
24 | private final ViewHandler viewHandler;
25 | private final PortalHandler portalHandler;
26 | private final CustomPortalHandler customPortalHandler;
27 |
28 | public LinkPortalCommand(ParentCommand parent,
29 | ViewHandler viewHandler, PortalHandler portalHandler,
30 | CustomPortalHandler customPortalHandler) {
31 |
32 | super("link", NetherViewPlugin.CUSTOM_PORTAL_PERM, true, parent);
33 | this.viewHandler = viewHandler;
34 | addArg(new Argument("from portal", ArgType.STRING));
35 | addArg(new Argument("to portal", ArgType.STRING));
36 |
37 | this.portalHandler = portalHandler;
38 | this.customPortalHandler = customPortalHandler;
39 | }
40 |
41 | @Override
42 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
43 |
44 | String portalName1 = arguments[0].getString();
45 | String portalName2 = arguments[1].getString();
46 |
47 | Portal portal1 = customPortalHandler.getPortal(portalName1);
48 | Portal portal2 = customPortalHandler.getPortal(portalName2);
49 |
50 | if (portal1 == null) {
51 | MessageUtils.sendInfo(sender, Message.NO_PORTAL_FOUND_WITH_NAME, portalName1);
52 | return;
53 | }
54 |
55 | if (portal2 == null) {
56 | MessageUtils.sendInfo(sender, Message.NO_PORTAL_FOUND_WITH_NAME, portalName2);
57 | return;
58 | }
59 |
60 | Player player = (Player) sender;
61 |
62 | try {
63 | viewHandler.removePortal(portal1);
64 | portalHandler.linkPortalTo(portal1, portal2, player);
65 | viewHandler.displayClosestPortalTo(player, player.getEyeLocation());
66 | MessageUtils.sendInfo(sender, Message.LINKED_PORTAL, portalName1, portalName2);
67 |
68 | } catch (MessageException e) {
69 | MessageUtils.sendInfo(sender, e.getPlayerMessage(), e.getPlaceholderValues());
70 | }
71 | }
72 |
73 | @Override
74 | public List getTabList(CommandSender sender, String[] arguments) {
75 |
76 | if (arguments.length <= 2) {
77 |
78 | return customPortalHandler.getPortalNames().stream().
79 | filter(name -> name.startsWith(arguments[arguments.length - 1])).
80 | collect(Collectors.toList());
81 | }
82 |
83 | return super.getTabList(sender, arguments);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/customportal/commands/UnlinkPortalCommand.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.customportal.commands;
2 |
3 | import me.gorgeousone.netherview.NetherViewPlugin;
4 | import me.gorgeousone.netherview.cmdframework.argument.ArgType;
5 | import me.gorgeousone.netherview.cmdframework.argument.ArgValue;
6 | import me.gorgeousone.netherview.cmdframework.argument.Argument;
7 | import me.gorgeousone.netherview.cmdframework.command.ArgCommand;
8 | import me.gorgeousone.netherview.cmdframework.command.ParentCommand;
9 | import me.gorgeousone.netherview.customportal.CustomPortalHandler;
10 | import me.gorgeousone.netherview.handlers.ViewHandler;
11 | import me.gorgeousone.netherview.message.Message;
12 | import me.gorgeousone.netherview.message.MessageUtils;
13 | import me.gorgeousone.netherview.portal.Portal;
14 | import org.bukkit.command.CommandSender;
15 |
16 | import java.util.List;
17 | import java.util.stream.Collectors;
18 |
19 | public class UnlinkPortalCommand extends ArgCommand {
20 |
21 | private final ViewHandler viewHandler;
22 | private final CustomPortalHandler customPortalHandler;
23 |
24 | public UnlinkPortalCommand(ParentCommand parent,
25 | ViewHandler viewHandler,
26 | CustomPortalHandler customPortalHandler) {
27 |
28 | super("unlink", NetherViewPlugin.CUSTOM_PORTAL_PERM, true, parent);
29 | this.viewHandler = viewHandler;
30 | addArg(new Argument("portal", ArgType.STRING));
31 |
32 | this.customPortalHandler = customPortalHandler;
33 | }
34 |
35 | @Override
36 | protected void onCommand(CommandSender sender, ArgValue[] arguments) {
37 |
38 | String portalName = arguments[0].getString();
39 |
40 | Portal portal = customPortalHandler.getPortal(portalName);
41 |
42 | if (portal == null) {
43 | MessageUtils.sendInfo(sender, Message.NO_PORTAL_FOUND_WITH_NAME, portalName);
44 | return;
45 | }
46 |
47 | if (!portal.isLinked()) {
48 | return;
49 | }
50 |
51 | viewHandler.removePortal(portal);
52 | portal.removeLink();
53 | MessageUtils.sendInfo(sender, Message.UNLINKED_PORTAL, portalName);
54 | }
55 |
56 | @Override
57 | public List getTabList(CommandSender sender, String[] arguments) {
58 |
59 | if (arguments.length <= 1) {
60 |
61 | return customPortalHandler.getPortalNames().stream().
62 | filter(name -> name.startsWith(arguments[arguments.length - 1])).
63 | collect(Collectors.toList());
64 | }
65 |
66 | return super.getTabList(sender, arguments);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/event/PortalLinkEvent.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.event;
2 |
3 | import me.gorgeousone.netherview.portal.Portal;
4 | import org.bukkit.entity.Player;
5 | import org.bukkit.event.Cancellable;
6 | import org.bukkit.event.Event;
7 | import org.bukkit.event.HandlerList;
8 |
9 | public class PortalLinkEvent extends Event implements Cancellable {
10 |
11 | private static final HandlerList HANDLER_LIST = new HandlerList();
12 | private boolean isCancelled;
13 |
14 | private final Player player;
15 | private final Portal fromPortal;
16 | private final Portal toPortal;
17 |
18 | public PortalLinkEvent(Portal fromPortal, Portal toPortal, Player player) {
19 |
20 | this.player = player;
21 | this.fromPortal = fromPortal;
22 | this.toPortal = toPortal;
23 | }
24 |
25 | public Player getPlayer() {
26 | return player;
27 | }
28 |
29 | public Portal getFromPortal() {
30 | return fromPortal;
31 | }
32 |
33 | public Portal getToPortal() {
34 | return toPortal;
35 | }
36 |
37 | @Override
38 | public boolean isCancelled() {
39 | return isCancelled;
40 | }
41 |
42 | @Override
43 | public void setCancelled(boolean isCancelled) {
44 | this.isCancelled = isCancelled;
45 | }
46 |
47 | @Override
48 | public HandlerList getHandlers() {
49 | return HANDLER_LIST;
50 | }
51 |
52 | public static HandlerList getHandlerList() {
53 | return HANDLER_LIST;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/event/PortalUnlinkEvent.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.event;
2 |
3 | import me.gorgeousone.netherview.portal.Portal;
4 | import org.bukkit.event.Event;
5 | import org.bukkit.event.HandlerList;
6 |
7 | public class PortalUnlinkEvent extends Event {
8 |
9 | private static final HandlerList HANDLER_LIST = new HandlerList();
10 |
11 | private final Portal fromPortal;
12 | private final Portal toPortal;
13 | private final UnlinkReason reason;
14 |
15 | public PortalUnlinkEvent(Portal portal, Portal linkedPortal, UnlinkReason reason) {
16 |
17 | this.fromPortal = portal;
18 | this.toPortal = linkedPortal;
19 | this.reason = reason;
20 | }
21 |
22 | public Portal getFromPortal() {
23 | return fromPortal;
24 | }
25 |
26 | public Portal getToPortal() {
27 | return toPortal;
28 | }
29 |
30 | public UnlinkReason getReason() {
31 | return reason;
32 | }
33 |
34 | @Override
35 | public HandlerList getHandlers() {
36 | return HANDLER_LIST;
37 | }
38 |
39 | public static HandlerList getHandlerList() {
40 | return HANDLER_LIST;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/event/UnlinkReason.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.event;
2 |
3 | public enum UnlinkReason {
4 | PORTAL_DESTROYED, LINKED_PORTAL_DESTROYED, SWITCHED_TARGET_PORTAL
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/AxisAlignedRect.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry;
2 |
3 | import me.gorgeousone.netherview.wrapper.Axis;
4 | import org.bukkit.util.Vector;
5 |
6 | /**
7 | * A rectangle used to describe the size of a nether portal (the part without the frame) or used as near plane in a view frustum.
8 | */
9 | public class AxisAlignedRect {
10 |
11 | private final Axis axis;
12 | private final Plane plane;
13 |
14 | private final Vector min;
15 | private final Vector max;
16 |
17 | public AxisAlignedRect(Axis axis, Vector min, Vector max) {
18 |
19 | this.axis = axis;
20 | this.min = min.clone();
21 | this.max = max.clone();
22 |
23 | if (axis == Axis.X) {
24 | plane = new Plane(min, new Vector(0, 0, 1));
25 |
26 | if (min.getZ() != max.getZ()) {
27 | throw new IllegalArgumentException("Z coordinates of x aligned portal must be equal");
28 | }
29 | } else {
30 | plane = new Plane(min, new Vector(1, 0, 0));
31 |
32 | if (min.getX() != max.getX()) {
33 | throw new IllegalArgumentException("Z coordinates of x aligned portal must be equal");
34 | }
35 | }
36 |
37 | if (height() < 0 || width() < 0) {
38 | throw new IllegalArgumentException("Rectangle maximum coordinates must be greater than minimum coordinates");
39 | }
40 | }
41 |
42 | public Axis getAxis() {
43 | return axis;
44 | }
45 |
46 | public Vector getMin() {
47 | return min.clone();
48 | }
49 |
50 | public Vector getMax() {
51 | return max.clone();
52 | }
53 |
54 | public double width() {
55 | return axis == Axis.X ?
56 | max.getX() - min.getX() :
57 | max.getZ() - min.getZ();
58 | }
59 |
60 | public double height() {
61 | return max.getY() - min.getY();
62 | }
63 |
64 | public AxisAlignedRect translate(Vector delta) {
65 | min.add(delta);
66 | max.add(delta);
67 | plane.translate(delta);
68 | return this;
69 | }
70 |
71 | public Plane getPlane() {
72 | return plane;
73 | }
74 |
75 | public boolean contains(Vector pointInPlane) {
76 |
77 | double pointY = pointInPlane.getY();
78 |
79 | if (pointY < min.getY() || pointY > max.getY()) {
80 | return false;
81 | }
82 |
83 | if (axis == Axis.X) {
84 | double pointX = pointInPlane.getX();
85 | return pointX >= min.getX() && pointX <= max.getX();
86 |
87 | } else {
88 | double pointZ = pointInPlane.getZ();
89 | return pointZ >= min.getZ() && pointZ <= max.getZ();
90 | }
91 | }
92 |
93 | @Override
94 | public AxisAlignedRect clone() {
95 | return new AxisAlignedRect(getAxis(), getMin(), getMax());
96 | }
97 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/BlockVec.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry;
2 |
3 | import com.comphenix.protocol.wrappers.BlockPosition;
4 | import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
5 | import org.bukkit.Location;
6 | import org.bukkit.World;
7 | import org.bukkit.block.Block;
8 | import org.bukkit.util.Vector;
9 |
10 | import java.util.Objects;
11 |
12 | /**
13 | * A simple 3D vector class with int coordinates only designed for storing block locations.
14 | */
15 | public class BlockVec {
16 |
17 | private int x;
18 | private int y;
19 | private int z;
20 |
21 | public BlockVec() {}
22 |
23 | public BlockVec(Block block) {
24 | this(block.getX(), block.getY(), block.getZ());
25 | }
26 |
27 | public BlockVec(Location location) {
28 | this(location.getBlockX(), location.getBlockY(), location.getBlockZ());
29 | }
30 |
31 | public BlockVec(Vector vector) {
32 | this(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
33 | }
34 |
35 | public BlockVec(BlockPosition blockPosition) {
36 | this(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
37 | }
38 |
39 | public BlockVec(ChunkCoordIntPair chunkLocation) {
40 | this(chunkLocation.getChunkX() << 4, 0, chunkLocation.getChunkZ() << 4);
41 | }
42 |
43 | public BlockVec(short posInChunk) {
44 | x = posInChunk >>> 8 & 0xF;
45 | y = posInChunk & 0xF;
46 | z = posInChunk >>> 4 & 0xF;
47 | }
48 |
49 | public BlockVec(int x, int y, int z) {
50 | this.x = x;
51 | this.y = y;
52 | this.z = z;
53 | }
54 |
55 | public int getX() {
56 | return x;
57 | }
58 |
59 | public BlockVec setX(int x) {
60 | this.x = x;
61 | return this;
62 | }
63 |
64 | public int getZ() {
65 | return z;
66 | }
67 |
68 | public BlockVec setZ(int z) {
69 | this.z = z;
70 | return this;
71 | }
72 |
73 | public int getY() {
74 | return y;
75 | }
76 |
77 | public BlockVec setY(int y) {
78 | this.y = y;
79 | return this;
80 | }
81 |
82 | public BlockVec add(BlockVec other) {
83 | return add(other.x, other.y, other.z);
84 | }
85 |
86 | public BlockVec add(int dx, int dy, int dz) {
87 | x += dx;
88 | y += dy;
89 | z += dz;
90 | return this;
91 | }
92 |
93 | public BlockVec subtract(BlockVec other) {
94 | x -= other.getX();
95 | y -= other.getY();
96 | z -= other.getZ();
97 | return this;
98 | }
99 |
100 | public BlockVec multiply(int multiplier) {
101 | x *= multiplier;
102 | y *= multiplier;
103 | z *= multiplier;
104 | return this;
105 | }
106 |
107 | public static BlockVec getMinimum(BlockVec v1, BlockVec v2) {
108 | return new BlockVec(
109 | Math.min(v1.x, v2.x),
110 | Math.min(v1.y, v2.y),
111 | Math.min(v1.z, v2.z));
112 | }
113 |
114 | public static BlockVec getMaximum(BlockVec v1, BlockVec v2) {
115 | return new BlockVec(
116 | Math.max(v1.x, v2.x),
117 | Math.max(v1.y, v2.y),
118 | Math.max(v1.z, v2.z));
119 | }
120 |
121 | public Vector toVector() {
122 | return new Vector(x, y, z);
123 | }
124 |
125 | public Location toLocation(World world) {
126 | return new Location(world, x, y, z);
127 | }
128 |
129 | public Block toBlock(World world) {
130 | return world.getBlockAt(x, y, z);
131 | }
132 |
133 | public BlockPosition toBlockPos() {
134 | return new BlockPosition(x, y, z);
135 | }
136 |
137 | @Override
138 | public BlockVec clone() {
139 | return new BlockVec(x, y, z);
140 | }
141 |
142 | @Override
143 | public boolean equals(Object o) {
144 | if (this == o) {
145 | return true;
146 | }
147 | if (!(o instanceof BlockVec)) {
148 | return false;
149 | }
150 | BlockVec otherVec = (BlockVec) o;
151 | return x == otherVec.x &&
152 | y == otherVec.y &&
153 | z == otherVec.z;
154 | }
155 |
156 | @Override
157 | public int hashCode() {
158 | return Objects.hash(x, y, z);
159 | }
160 |
161 | public static String toSimpleString(Location loc) {
162 | return "[" + loc.getWorld().getName() + ", " + loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + "]";
163 | }
164 |
165 | @Override
166 | public String toString() {
167 | return "[" + x + ", " + y + ", " + z + "]";
168 | }
169 |
170 | public String serialize() {
171 | return "x=" + x + ",y=" + y + ",z=" + z;
172 | }
173 |
174 | /**
175 | * Converts the vector into a short that represents it's position relative to the chunk it's in.
176 | * Used by the MultiBlockChangePacket introduced in 1.16.2
177 | */
178 | public short toChunkShort() {
179 |
180 | return (short) ((x & 0xF) << 8 |
181 | (z & 0xF) << 4 |
182 | (y & 0xF));
183 | }
184 |
185 | public static BlockVec fromString(String serialized) {
186 |
187 | if (serialized.length() < 11) {
188 | throw new IllegalArgumentException("Cannot deserialize BlockVec from string " + serialized + ": String is too short.");
189 | }
190 |
191 | String coordinateString = serialized;
192 |
193 | //So I decided to remove the square brackets from the BlockVec string in v1.2.1
194 | //but for migrating from an earlier plugin version I will have to leave this check in
195 | if (coordinateString.startsWith("[")) {
196 | coordinateString = coordinateString.substring(1, coordinateString.length() - 1);
197 | }
198 |
199 | String[] coordinates = coordinateString.split(",");
200 |
201 | if (coordinates.length != 3) {
202 | throw new IllegalArgumentException("Cannot deserialize BlockVec from string " + serialized + ": String contains too few coordinates.");
203 | }
204 |
205 | try {
206 | int x = Integer.parseInt(coordinates[0].substring(2));
207 | int y = Integer.parseInt(coordinates[1].substring(2));
208 | int z = Integer.parseInt(coordinates[2].substring(2));
209 | return new BlockVec(x, y, z);
210 |
211 | } catch (NumberFormatException e) {
212 | throw new IllegalArgumentException("Cannot deserialize BlockVec from string " + serialized + ": " + e.getMessage());
213 | }
214 | }
215 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/Cuboid.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry;
2 |
3 | import org.bukkit.block.Block;
4 |
5 | public class Cuboid {
6 |
7 | private final BlockVec min;
8 | private final BlockVec max;
9 |
10 | public Cuboid(BlockVec pos1, BlockVec pos2) {
11 |
12 | this.min = BlockVec.getMinimum(pos1, pos2);
13 | this.max = BlockVec.getMaximum(pos1, pos2);
14 | }
15 |
16 | public BlockVec getMin() {
17 | return min.clone();
18 | }
19 |
20 | public BlockVec getMax() {
21 | return max.clone();
22 | }
23 |
24 | public int getWidthX() {
25 | return max.getX() - min.getX();
26 | }
27 |
28 | public int getHeight() {
29 | return max.getY() - min.getY();
30 | }
31 |
32 | public int getWidthZ() {
33 | return max.getZ() - min.getZ();
34 | }
35 |
36 | public boolean contains(BlockVec vec) {
37 | return vec.getX() >= min.getX() && vec.getX() < max.getX() &&
38 | vec.getY() >= min.getY() && vec.getY() < max.getY() &&
39 | vec.getZ() >= min.getZ() && vec.getZ() < max.getZ();
40 | }
41 |
42 | public boolean contains(Block block) {
43 | return block.getX() >= min.getX() && block.getX() < max.getX() &&
44 | block.getY() >= min.getY() && block.getY() < max.getY() &&
45 | block.getZ() >= min.getZ() && block.getZ() < max.getZ();
46 | }
47 |
48 | public Cuboid translateMin(int dx, int dy, int dz) {
49 | min.add(dx, dy, dz);
50 | return this;
51 | }
52 |
53 | public Cuboid translateMin(BlockVec vec) {
54 | min.add(vec);
55 | return this;
56 | }
57 |
58 | public Cuboid translateMax(int dx, int dy, int dz) {
59 | max.add(dx, dy, dz);
60 | return this;
61 | }
62 |
63 | public Cuboid translateMax(BlockVec vec) {
64 | max.add(vec);
65 | return this;
66 | }
67 |
68 | public boolean intersects(Cuboid otherBox) {
69 | return intersectsX(otherBox) && intersectsY(otherBox) && intersectsZ(otherBox) ||
70 | otherBox.intersectsX(this) && otherBox.intersectsY(this) && otherBox.intersectsZ(this);
71 | }
72 |
73 | public boolean intersectsX(Cuboid otherBox) {
74 | return containsX(otherBox.min.getX()) || containsX(otherBox.max.getX() - 1) || otherBox.min.getX() < min.getX() && otherBox.max.getX() > max.getX();
75 | }
76 |
77 | public boolean intersectsY(Cuboid otherBox) {
78 | return containsY(otherBox.min.getY()) || containsY(otherBox.max.getY() - 1) || otherBox.min.getY() < min.getY() && otherBox.max.getY() > max.getY();
79 | }
80 |
81 | public boolean intersectsZ(Cuboid otherBox) {
82 | return containsZ(otherBox.min.getZ()) || containsZ(otherBox.max.getZ() - 1) || otherBox.min.getZ() < min.getZ() && otherBox.max.getZ() > max.getZ();
83 | }
84 |
85 | public boolean containsX(double x) {
86 | return x >= min.getX() && x < max.getX();
87 | }
88 |
89 | public boolean containsY(double y) {
90 | return y >= min.getY() && y < max.getY();
91 | }
92 |
93 | public boolean containsZ(double z) {
94 | return z >= min.getZ() && z <= max.getZ();
95 | }
96 |
97 | @Override
98 | public Cuboid clone() {
99 | return new Cuboid(min, max);
100 | }
101 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/Line.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry;
2 |
3 | import org.bukkit.util.Vector;
4 |
5 | public class Line {
6 |
7 | private final Vector origin;
8 | private final Vector direction;
9 |
10 | public Line(Vector point1, Vector point2) {
11 |
12 | this.origin = point1.clone();
13 | this.direction = point2.clone().subtract(point1);
14 | }
15 |
16 | public Vector getOrigin() {
17 | return origin.clone();
18 | }
19 |
20 | public Vector getDirection() {
21 | return direction.clone();
22 | }
23 |
24 | public Vector getPoint(double d) {
25 | return getOrigin().add(getDirection().multiply(d));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/Plane.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry;
2 |
3 | import org.bukkit.util.Vector;
4 |
5 | public class Plane {
6 |
7 | private final Vector origin;
8 | private final Vector normal;
9 |
10 | public Plane(Vector origin, Vector normal) {
11 |
12 | this.origin = origin.clone();
13 | this.normal = normal.clone().normalize();
14 |
15 | if (normal.lengthSquared() == 0) {
16 | throw new IllegalArgumentException("normal cannot be 0");
17 | }
18 | }
19 |
20 | public Vector getOrigin() {
21 | return origin.clone();
22 | }
23 |
24 | public Vector getNormal() {
25 | return normal.clone();
26 | }
27 |
28 | public boolean contains(Vector point) {
29 |
30 | if (point == null) {
31 | return false;
32 | }
33 |
34 | Vector relPoint = getOrigin().subtract(point);
35 | return Math.abs(getNormal().dot(relPoint)) < 0.0001;
36 | }
37 |
38 | public Vector getIntersection(Line line) {
39 |
40 | Vector normal = getNormal();
41 |
42 | double d = getOrigin().subtract(line.getOrigin()).dot(normal) / line.getDirection().dot(normal);
43 | Vector intersection = line.getPoint(d);
44 |
45 | return contains(intersection) ? intersection : null;
46 | }
47 |
48 | public void translate(Vector delta) {
49 | origin.add(delta);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/viewfrustum/DefinedLine.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry.viewfrustum;
2 |
3 | import me.gorgeousone.netherview.geometry.Line;
4 | import org.bukkit.util.Vector;
5 |
6 | /**
7 | * An euclidean line where only points between the given start and end can be accessed.
8 | */
9 | public class DefinedLine extends Line {
10 |
11 | public DefinedLine(Vector start, Vector end) {
12 | super(start, end);
13 | }
14 |
15 | @Override
16 | public Vector getPoint(double d) {
17 | return d < 0 || d > 1 ? null : super.getPoint(d);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/geometry/viewfrustum/ViewFrustumFactory.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.geometry.viewfrustum;
2 |
3 | import me.gorgeousone.netherview.geometry.AxisAlignedRect;
4 | import me.gorgeousone.netherview.geometry.Line;
5 | import me.gorgeousone.netherview.geometry.Plane;
6 | import me.gorgeousone.netherview.portal.Portal;
7 | import me.gorgeousone.netherview.wrapper.Axis;
8 | import org.bukkit.entity.Player;
9 | import org.bukkit.util.Vector;
10 |
11 | public final class ViewFrustumFactory {
12 |
13 | private ViewFrustumFactory() {}
14 |
15 | /**
16 | * Returns a viewing frustum with a near plane precisely representing the area the player can see through the portal.
17 | */
18 | public static ViewFrustum createFrustum(Vector viewPoint, AxisAlignedRect portalRect, int frustumLength) {
19 |
20 | boolean isPlayerBehindPortal = isPlayerBehindPortal(viewPoint, portalRect);
21 |
22 | Vector portalNormal = portalRect.getPlane().getNormal();
23 | Vector playerFacingToPortal = portalNormal.multiply(isPlayerBehindPortal ? 1 : -1);
24 |
25 | //this will become near plane of the viewing frustum. It will be cropped to fit the actual player view through the portal
26 | AxisAlignedRect totalViewingRect = portalRect.clone().translate(playerFacingToPortal.clone().multiply(0.5));
27 | Plane portalPlane = totalViewingRect.getPlane();
28 |
29 | Vector viewingRectMin = totalViewingRect.getMin();
30 | Vector viewingRectMax = totalViewingRect.getMax();
31 |
32 | addTolerance(viewingRectMin, viewingRectMax, portalRect, 0.15);
33 |
34 | //depending on which portal frame blocks will block the view, the viewing rect bounds are contracted by casting rays along the block edges
35 | //here for the height of the rect...
36 | if (viewPoint.getY() < viewingRectMin.getY()) {
37 |
38 | Vector closeRectMin = viewingRectMin.clone().subtract(playerFacingToPortal);
39 | Vector newRectMin = portalPlane.getIntersection(new Line(viewPoint, closeRectMin));
40 | viewingRectMin.setY(newRectMin.getY());
41 |
42 | } else if (viewPoint.getY() > viewingRectMax.getY()) {
43 |
44 | Vector closeRectMax = viewingRectMax.clone().subtract(playerFacingToPortal);
45 | Vector newRectMax = portalPlane.getIntersection(new Line(viewPoint, closeRectMax));
46 | viewingRectMax.setY(newRectMax.getY());
47 | }
48 |
49 | Axis portalAxis = portalRect.getAxis();
50 |
51 | //... also for the width
52 | if (portalAxis == Axis.X) {
53 |
54 | if (viewPoint.getX() < viewingRectMin.getX()) {
55 |
56 | Vector closeRectMin = viewingRectMin.clone().subtract(playerFacingToPortal);
57 | Vector newRectMin = portalPlane.getIntersection(new Line(viewPoint, closeRectMin));
58 | viewingRectMin.setX(newRectMin.getX());
59 |
60 | } else if (viewPoint.getX() > viewingRectMax.getX()) {
61 |
62 | Vector closeRectMax = viewingRectMax.clone().subtract(playerFacingToPortal);
63 | Vector newRectMax = portalPlane.getIntersection(new Line(viewPoint, closeRectMax));
64 | viewingRectMax.setX(newRectMax.getX());
65 | }
66 |
67 | } else {
68 |
69 | if (viewPoint.getZ() < viewingRectMin.getZ()) {
70 |
71 | Vector closeRectMin = viewingRectMin.clone().subtract(playerFacingToPortal);
72 | Vector newRectMin = portalPlane.getIntersection(new Line(viewPoint, closeRectMin));
73 | viewingRectMin.setZ(newRectMin.getZ());
74 |
75 | } else if (viewPoint.getZ() > viewingRectMax.getZ()) {
76 |
77 | Vector closeRectMax = viewingRectMax.clone().subtract(playerFacingToPortal);
78 | Vector newRectMax = portalPlane.getIntersection(new Line(viewPoint, closeRectMax));
79 | viewingRectMax.setZ(newRectMax.getZ());
80 | }
81 | }
82 |
83 | try {
84 | AxisAlignedRect actualViewingRect = new AxisAlignedRect(totalViewingRect.getAxis(), viewingRectMin, viewingRectMax);
85 | return new ViewFrustum(viewPoint, actualViewingRect, frustumLength);
86 |
87 | } catch (IllegalArgumentException e) {
88 | return null;
89 | }
90 | }
91 |
92 | public static boolean isPlayerBehindPortal(Player player, Portal portal) {
93 | return isPlayerBehindPortal(player.getEyeLocation().toVector(), portal.getPortalRect());
94 | }
95 |
96 | public static boolean isPlayerBehindPortal(Vector viewPoint, AxisAlignedRect portalRect) {
97 |
98 | Vector portalPos = portalRect.getMin();
99 |
100 | return portalRect.getAxis() == Axis.X ?
101 | viewPoint.getZ() < portalPos.getZ() :
102 | viewPoint.getX() < portalPos.getX();
103 | }
104 |
105 | //widen the rectangle bounds a bit so the projection becomes smoother/more consistent when moving quickly
106 | //side effects are blocks slightly sticking out at the sides when standing further away
107 | private static void addTolerance(Vector rectMin, Vector rectMax, AxisAlignedRect portalRect, double tolerance) {
108 |
109 | Vector toleranceVec = portalRect.getAxis().getCrossNormal();
110 | toleranceVec.setY(1);
111 | toleranceVec.multiply(tolerance);
112 |
113 | rectMin.subtract(toleranceVec);
114 | rectMax.add(toleranceVec);
115 | }
116 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/handlers/EntityVisibilityHandler.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.handlers;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.blockcache.BlockCache;
5 | import me.gorgeousone.netherview.blockcache.ProjectionCache;
6 | import me.gorgeousone.netherview.blockcache.Transform;
7 | import me.gorgeousone.netherview.message.MessageUtils;
8 | import me.gorgeousone.netherview.packet.PacketHandler;
9 | import me.gorgeousone.netherview.portal.ProjectionEntity;
10 | import org.bukkit.Location;
11 | import org.bukkit.entity.Entity;
12 | import org.bukkit.entity.Player;
13 | import org.bukkit.plugin.java.JavaPlugin;
14 | import org.bukkit.scheduler.BukkitRunnable;
15 |
16 | import java.util.Collection;
17 | import java.util.HashMap;
18 | import java.util.HashSet;
19 | import java.util.Map;
20 | import java.util.Set;
21 |
22 | /**
23 | * A class that runs a BukkitRunnable to check on entities nearby portals and their movements.
24 | * It will show/hide entities that walk in/out of areas viewed by players from portals.
25 | * It also creates movement animations for projected entities for players.
26 | */
27 | public class EntityVisibilityHandler {
28 |
29 | private final JavaPlugin plugin;
30 | private final ConfigSettings configSettings;
31 |
32 | private final ViewHandler viewHandler;
33 | private final PacketHandler packetHandler;
34 |
35 | private BukkitRunnable entityMotionChecker;
36 | private final Map projectionEntities;
37 |
38 | public EntityVisibilityHandler(JavaPlugin plugin,
39 | ConfigSettings configSettings,
40 | ViewHandler viewHandler,
41 | PacketHandler packetHandler) {
42 |
43 | this.plugin = plugin;
44 | this.configSettings = configSettings;
45 | this.viewHandler = viewHandler;
46 | this.packetHandler = packetHandler;
47 |
48 | projectionEntities = new HashMap<>();
49 |
50 | if (configSettings.isEntityHidingEnabled()) {
51 | startEntityCheckerChecker();
52 | }
53 | }
54 |
55 | public void reload() {
56 |
57 | disable();
58 |
59 | if (configSettings.isEntityHidingEnabled()) {
60 | startEntityCheckerChecker();
61 | }
62 | }
63 |
64 | public void disable() {
65 |
66 | entityMotionChecker.cancel();
67 | projectionEntities.clear();
68 | }
69 |
70 | private void startEntityCheckerChecker() {
71 |
72 | MessageUtils.printDebug("Starting entity visibility timer");
73 |
74 | entityMotionChecker = new BukkitRunnable() {
75 | @Override
76 | public void run() {
77 |
78 | Map> portalSideViewers = viewHandler.getSessionsSortedByPortalSides();
79 | handleRealEntitiesVisibility(portalSideViewers);
80 |
81 | if (!configSettings.isEntityViewingEnabled()) {
82 | return;
83 | }
84 |
85 | Map> watchedBlockCaches = getProjectionsSortedByBlockCaches(portalSideViewers.keySet());
86 | handleProjectionEntitiesVisibility(portalSideViewers, watchedBlockCaches);
87 |
88 | projectionEntities.entrySet().removeIf(entry -> entry.getKey().isDead());
89 | projectionEntities.values().forEach(ProjectionEntity::updateLastLoc);
90 | }
91 | };
92 |
93 | entityMotionChecker.runTaskTimer(plugin, 0, configSettings.getEntityUpdateTicks());
94 | }
95 |
96 | private void handleRealEntitiesVisibility(Map> portalSideViewers) {
97 |
98 | for (ProjectionCache projection : portalSideViewers.keySet()) {
99 |
100 | Set currentEntities = projection.getEntities();
101 |
102 | for (PlayerViewSession session : portalSideViewers.get(projection)) {
103 |
104 | showEntitiesNextToFrustum(session);
105 | hideEntitiesInFrustum(session, currentEntities);
106 | }
107 | }
108 | }
109 |
110 | private Map> getProjectionsSortedByBlockCaches(Set projections) {
111 |
112 | Map> sortedSources = new HashMap<>();
113 |
114 | for (ProjectionCache projection : projections) {
115 |
116 | BlockCache source = projection.getSourceCache();
117 | sortedSources.computeIfAbsent(source, set -> new HashSet<>());
118 | sortedSources.get(source).add(projection);
119 | }
120 |
121 | return sortedSources;
122 | }
123 |
124 | private void handleProjectionEntitiesVisibility(Map> portalSideViewers,
125 | Map> watchedBlockCaches) {
126 |
127 | for (BlockCache blockCache : watchedBlockCaches.keySet()) {
128 |
129 | Set currentEntities = blockCache.getEntities();
130 | Set newEntities = getProjectionEntities(currentEntities);
131 |
132 | currentEntities.forEach(entity -> projectionEntities.computeIfAbsent(entity, ProjectionEntity::new));
133 |
134 | Map movingEntities = getMovingEntities(currentEntities);
135 |
136 | for (ProjectionCache projection : watchedBlockCaches.get(blockCache)) {
137 | for (PlayerViewSession session : portalSideViewers.get(projection)) {
138 |
139 | hideEntitiesOutsideProjection(session, newEntities);
140 |
141 | if (session.getLastViewFrustum() == null) {
142 | continue;
143 | }
144 |
145 | projectNewEntitiesInsideProjection(session, newEntities);
146 | displayEntityMovements(session, movingEntities);
147 | }
148 | }
149 | }
150 | }
151 |
152 | private Set getProjectionEntities(Set entities) {
153 |
154 | Set projectionEntities = new HashSet<>();
155 |
156 | entities.forEach(entity -> {
157 | this.projectionEntities.computeIfAbsent(entity, ProjectionEntity::new);
158 | projectionEntities.add(this.projectionEntities.get(entity));
159 | });
160 |
161 | return projectionEntities;
162 | }
163 |
164 | private void hideEntitiesOutsideProjection(PlayerViewSession session, Set newEntities) {
165 |
166 | for (ProjectionEntity entityProjection : new HashSet<>(session.getProjectedEntities())) {
167 |
168 | if (!newEntities.contains(entityProjection) || !session.isVisibleInProjection(entityProjection.getEntity())) {
169 | viewHandler.destroyProjectedEntity(session.getPlayer(), entityProjection);
170 | }
171 | }
172 | }
173 |
174 | private void projectNewEntitiesInsideProjection(PlayerViewSession session, Set newEntities) {
175 |
176 | for (ProjectionEntity entityProjection : newEntities) {
177 |
178 | if (!session.getProjectedEntities().contains(entityProjection) && session.isVisibleInProjection(entityProjection.getEntity())) {
179 | viewHandler.projectEntity(session.getPlayer(), entityProjection, session.getViewedPortalSide().getLinkTransform());
180 | }
181 | }
182 | }
183 |
184 | private Map getMovingEntities(Collection entities) {
185 |
186 | Map movingEntities = new HashMap<>();
187 |
188 | for (Entity entity : entities) {
189 |
190 | ProjectionEntity projectionEntity = projectionEntities.get(entity);
191 | Location lastLoc = projectionEntity.getLastLoc();
192 |
193 | if (lastLoc == null) {
194 | continue;
195 | }
196 |
197 | Location newLoc = entity.getLocation();
198 |
199 | if (newLoc.getWorld() == lastLoc.getWorld() && !newLoc.equals(lastLoc)) {
200 | movingEntities.put(projectionEntity, newLoc.clone().subtract(lastLoc));
201 | }
202 | }
203 |
204 | return movingEntities;
205 | }
206 |
207 | private void displayEntityMovements(PlayerViewSession session, Map movedEntities) {
208 |
209 | Player player = session.getPlayer();
210 | ProjectionCache projection = session.getViewedPortalSide();
211 | Transform linkTransform = projection.getLinkTransform();
212 | Set projectedEntities = session.getProjectedEntities();
213 |
214 | for (ProjectionEntity entity : movedEntities.keySet()) {
215 |
216 | boolean entityIsVisibleInProjection = session.isVisibleInProjection(entity.getEntity());
217 |
218 | if (!projectedEntities.contains(entity)) {
219 |
220 | if (entityIsVisibleInProjection) {
221 | viewHandler.projectEntity(session.getPlayer(), entity, linkTransform);
222 | }
223 | continue;
224 | }
225 |
226 | if (entityIsVisibleInProjection) {
227 |
228 | Location newEntityLoc = entity.getEntity().getLocation();
229 | Location lastEntityLoc = entity.getLastLoc();
230 |
231 | Location projectedMovement = linkTransform.rotateLoc(newEntityLoc.clone().subtract(lastEntityLoc));
232 |
233 | packetHandler.sendEntityMoveLook(
234 | session.getPlayer(),
235 | entity,
236 | projectedMovement.toVector(),
237 | projectedMovement.getYaw(),
238 | projectedMovement.getPitch(),
239 | entity.getEntity().isOnGround());
240 |
241 | } else {
242 | viewHandler.destroyProjectedEntity(player, entity);
243 | }
244 | }
245 | }
246 |
247 | private void showEntitiesNextToFrustum(PlayerViewSession session) {
248 |
249 | for (Entity entity : new HashSet<>(session.getHiddenEntities())) {
250 |
251 | if (!session.isHiddenBehindProjection(entity)) {
252 | viewHandler.showEntity(session.getPlayer(), entity);
253 | }
254 | }
255 | }
256 |
257 | private void hideEntitiesInFrustum(PlayerViewSession session, Set newEntities) {
258 |
259 | for (Entity entity : newEntities) {
260 |
261 | if (!session.getHiddenEntities().contains(entity) && session.isHiddenBehindProjection(entity)) {
262 | viewHandler.hideEntity(session.getPlayer(), entity);
263 | }
264 | }
265 | }
266 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/handlers/PlayerViewSession.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.handlers;
2 |
3 | import me.gorgeousone.netherview.blockcache.ProjectionCache;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.geometry.viewfrustum.ViewFrustum;
6 | import me.gorgeousone.netherview.portal.Portal;
7 | import me.gorgeousone.netherview.portal.ProjectionEntity;
8 | import me.gorgeousone.netherview.wrapper.WrappedBoundingBox;
9 | import me.gorgeousone.netherview.wrapper.blocktype.BlockType;
10 | import org.bukkit.Bukkit;
11 | import org.bukkit.Location;
12 | import org.bukkit.entity.Entity;
13 | import org.bukkit.entity.Player;
14 |
15 | import java.util.HashMap;
16 | import java.util.HashSet;
17 | import java.util.Map;
18 | import java.util.Set;
19 | import java.util.UUID;
20 |
21 | public class PlayerViewSession {
22 |
23 | private final UUID playerId;
24 | private final Portal viewedPortal;
25 |
26 | private ProjectionCache viewedPortalSide;
27 | private ViewFrustum lastViewFrustum;
28 |
29 | private final Map projectedBlocks;
30 | private final Set hiddenEntities;
31 | private final Set projectedEntities;
32 |
33 | public PlayerViewSession(Player player, Portal viewedPortal) {
34 |
35 | this.playerId = player.getUniqueId();
36 | this.viewedPortal = viewedPortal;
37 |
38 | this.projectedBlocks = new HashMap<>();
39 | this.hiddenEntities = new HashSet<>();
40 | this.projectedEntities = new HashSet<>();
41 | }
42 |
43 | public UUID getPlayerId() {
44 | return playerId;
45 | }
46 |
47 | public Player getPlayer() {
48 | return Bukkit.getPlayer(playerId);
49 | }
50 |
51 | public Portal getViewedPortal() {
52 | return viewedPortal;
53 | }
54 |
55 | /**
56 | * Returns the one of the two projection caches of a portal that is being displayed to the player in the portal view.
57 | */
58 | public ProjectionCache getViewedPortalSide() {
59 | return viewedPortalSide;
60 | }
61 |
62 | public void setViewedPortalSide(ProjectionCache viewedPortalSide) {
63 | this.viewedPortalSide = viewedPortalSide;
64 | }
65 |
66 | public ViewFrustum getLastViewFrustum() {
67 | return lastViewFrustum;
68 | }
69 |
70 | /**
71 | * Returns the latest view frustum that was used to calculate the projection blocks for the player's projection.
72 | * Returns null if player is not nearby any portal or no blocks were displayed (due to steep view angles)
73 | */
74 | public void setLastViewFrustum(ViewFrustum lastViewFrustum) {
75 | this.lastViewFrustum = lastViewFrustum;
76 | }
77 |
78 | /**
79 | * Returns a Map of BlockTypes linked to their location that are currently displayed with fake blocks to the player.
80 | * The method is being used very frequently so it does not check for the player's permission to view portal projections.
81 | * Returns (and internally adds) an empty Map if no projected blocks found.
82 | */
83 | public Map getProjectedBlocks() {
84 | return projectedBlocks;
85 | }
86 |
87 | public Set getHiddenEntities() {
88 | return hiddenEntities;
89 | }
90 |
91 | public Set getProjectedEntities() {
92 | return projectedEntities;
93 | }
94 |
95 | public boolean isHiddenBehindProjection(Entity entity) {
96 |
97 | if (lastViewFrustum == null) {
98 | return false;
99 | }
100 |
101 | WrappedBoundingBox boundingBox = WrappedBoundingBox.of(entity, entity.getLocation());
102 | return boundingBox.intersectsBlockCache(viewedPortalSide) && boundingBox.intersectsFrustum(lastViewFrustum);
103 | }
104 |
105 | public boolean isVisibleInProjection(Entity entity) {
106 |
107 | if (lastViewFrustum == null) {
108 | return false;
109 | }
110 |
111 | Location projectionLoc = viewedPortalSide.getLinkTransform().transformLoc(entity.getLocation());
112 | WrappedBoundingBox boundingBox = WrappedBoundingBox.of(entity, projectionLoc);
113 |
114 | return boundingBox.intersectsBlockCache(viewedPortalSide) && boundingBox.intersectsFrustum(lastViewFrustum);
115 | }
116 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/listeners/PlayerMoveListener.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.listeners;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.blockcache.Transform;
6 | import me.gorgeousone.netherview.customportal.CustomPortal;
7 | import me.gorgeousone.netherview.customportal.CustomPortalHandler;
8 | import me.gorgeousone.netherview.handlers.ViewHandler;
9 | import me.gorgeousone.netherview.utils.TeleportUtils;
10 | import org.bukkit.GameMode;
11 | import org.bukkit.Location;
12 | import org.bukkit.Material;
13 | import org.bukkit.World;
14 | import org.bukkit.entity.Player;
15 | import org.bukkit.event.EventHandler;
16 | import org.bukkit.event.Listener;
17 | import org.bukkit.event.player.PlayerGameModeChangeEvent;
18 | import org.bukkit.event.player.PlayerMoveEvent;
19 | import org.bukkit.event.player.PlayerToggleSneakEvent;
20 | import org.bukkit.plugin.java.JavaPlugin;
21 | import org.bukkit.scheduler.BukkitRunnable;
22 | import org.bukkit.util.Vector;
23 |
24 | import java.util.HashSet;
25 | import java.util.Set;
26 | import java.util.UUID;
27 |
28 | /**
29 | * A listener class that informs the view handler to update the portal view for players when they move.
30 | * It also takes care of the instant teleportation feature.
31 | */
32 | public class PlayerMoveListener implements Listener {
33 |
34 | private final JavaPlugin plugin;
35 | private final ConfigSettings configSettings;
36 | private final ViewHandler viewHandler;
37 | private final CustomPortalHandler customPortalHandler;
38 | private final Material portalMaterial;
39 |
40 | private final Set teleportedPlayers;
41 |
42 | public PlayerMoveListener(NetherViewPlugin plugin,
43 | ConfigSettings configSettings, ViewHandler viewHandler,
44 | CustomPortalHandler customPortalHandler,
45 | Material portalMaterial) {
46 |
47 | this.plugin = plugin;
48 | this.configSettings = configSettings;
49 | this.viewHandler = viewHandler;
50 | this.customPortalHandler = customPortalHandler;
51 | this.portalMaterial = portalMaterial;
52 |
53 | teleportedPlayers = new HashSet<>();
54 | }
55 |
56 | @EventHandler
57 | public void onPlayerMove(PlayerMoveEvent event) {
58 |
59 | Location from = event.getFrom();
60 | Location to = event.getTo();
61 |
62 | Vector fromVec = from.toVector();
63 | Vector toVec = to.toVector();
64 |
65 | if (fromVec.equals(toVec)) {
66 | return;
67 | }
68 |
69 | Player player = event.getPlayer();
70 | World world = player.getWorld();
71 | boolean enteredNewBlock = playerEnteredNewBlock(from, to);
72 |
73 | if (enteredNewBlock && configSettings.canCreatePortalViews(world)) {
74 |
75 | if (handleCustomPortalTp(player, from, to)) {
76 | return;
77 | }
78 | }
79 |
80 | if (configSettings.canCreatePortalViews(world)) {
81 |
82 | //sets player temporarily invulnerable so the game will instantly teleport them on entering a portal
83 | if (enteredNewBlock && configSettings.isInstantTeleportEnabled() && mortalEnteredPortal(player, from, to)) {
84 | TeleportUtils.setTemporarilyInvulnerable(player, plugin, 2);
85 | }
86 |
87 | handlePortalViewingOnMove(player, fromVec, toVec);
88 | }
89 | }
90 |
91 | /**
92 | * Teleports player if they entered a block of custom portal and it's not already their destination portal
93 | */
94 | private boolean handleCustomPortalTp(Player player, Location from, Location to) {
95 |
96 | CustomPortal portal = customPortalHandler.getPortalAt(to);
97 | UUID playerId = player.getUniqueId();
98 |
99 | if (portal == null) {
100 | teleportedPlayers.remove(playerId);
101 | return false;
102 | }
103 |
104 | if (teleportedPlayers.contains(playerId) || !portal.isLinked()) {
105 | return false;
106 | }
107 |
108 | viewHandler.hidePortalProjection(player);
109 | Transform tpTransform = portal.getTpTransform();
110 |
111 | Location destination = tpTransform.transformLoc(to.clone());
112 | destination.setWorld(portal.getCounterPortal().getWorld());
113 | player.teleport(destination);
114 | teleportedPlayers.add(playerId);
115 |
116 | Vector vel = to.toVector().subtract(from.toVector());
117 | Vector transformedVel = tpTransform.rotateVec(vel);
118 |
119 | new BukkitRunnable() {
120 | @Override
121 | public void run() {
122 | player.setVelocity(transformedVel);
123 | }
124 | }.runTask(plugin);
125 | return true;
126 | }
127 |
128 | /**
129 | * Checks if the player in a vulnerable game mode (so they would usually not be teleported instantly by a nether portal) and if they are entering a nether portal.
130 | */
131 | private boolean mortalEnteredPortal(Player player, Location from, Location to) {
132 |
133 | GameMode gameMode = player.getGameMode();
134 |
135 | return (gameMode == GameMode.SURVIVAL || gameMode == GameMode.ADVENTURE) &&
136 | to.getBlock().getType() == portalMaterial &&
137 | from.getBlock().getType() != portalMaterial;
138 | }
139 |
140 | private void handlePortalViewingOnMove(Player player, Vector fromVec, Vector toVec) {
141 |
142 | if (!teleportedPlayers.contains(player.getUniqueId()) &&
143 | player.getGameMode() != GameMode.SPECTATOR &&
144 | viewHandler.hasPortalViewEnabled(player) &&
145 | player.hasPermission(NetherViewPlugin.VIEW_PERM)) {
146 |
147 | Vector playerMovement = fromVec.clone().subtract(toVec);
148 | viewHandler.displayClosestPortalTo(player, player.getEyeLocation().add(playerMovement));
149 | }
150 | }
151 |
152 | private boolean playerEnteredNewBlock(Location from, Location to) {
153 |
154 | return (int) from.getX() != (int) to.getX() ||
155 | (int) from.getY() != (int) to.getY() ||
156 | (int) from.getZ() != (int) to.getZ();
157 | }
158 |
159 | @EventHandler
160 | public void onPlayerSneak(PlayerToggleSneakEvent event) {
161 |
162 | Player player = event.getPlayer();
163 |
164 | if (!viewHandler.hasViewSession(player)) {
165 | return;
166 | }
167 |
168 | new BukkitRunnable() {
169 | @Override
170 | public void run() {
171 | viewHandler.displayClosestPortalTo(player, player.getEyeLocation());
172 | }
173 | }.runTaskLater(plugin, 2);
174 | }
175 |
176 | @EventHandler
177 | public void onGameModeChange(PlayerGameModeChangeEvent event) {
178 |
179 | if (event.getNewGameMode() == GameMode.SPECTATOR) {
180 | viewHandler.hidePortalProjection(event.getPlayer());
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/listeners/PlayerQuitListener.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.listeners;
2 |
3 | import me.gorgeousone.netherview.customportal.PlayerSelectionHandler;
4 | import me.gorgeousone.netherview.handlers.ViewHandler;
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.EventHandler;
7 | import org.bukkit.event.Listener;
8 | import org.bukkit.event.player.PlayerQuitEvent;
9 |
10 | public class PlayerQuitListener implements Listener {
11 |
12 | private final ViewHandler viewHandler;
13 | private final PlayerSelectionHandler selectionHandler;
14 |
15 | public PlayerQuitListener(ViewHandler viewHandler,
16 | PlayerSelectionHandler selectionHandler) {
17 | this.viewHandler = viewHandler;
18 | this.selectionHandler = selectionHandler;
19 | }
20 |
21 | @EventHandler
22 | public void onQuit(PlayerQuitEvent event) {
23 |
24 | Player player = event.getPlayer();
25 | viewHandler.unregisterPlayer(player);
26 | selectionHandler.removeSelection(player);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/listeners/PlayerTeleportListener.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.listeners;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.NetherViewPlugin;
5 | import me.gorgeousone.netherview.event.PortalUnlinkEvent;
6 | import me.gorgeousone.netherview.event.UnlinkReason;
7 | import me.gorgeousone.netherview.geometry.BlockVec;
8 | import me.gorgeousone.netherview.handlers.PortalHandler;
9 | import me.gorgeousone.netherview.handlers.ViewHandler;
10 | import me.gorgeousone.netherview.message.Message;
11 | import me.gorgeousone.netherview.message.MessageException;
12 | import me.gorgeousone.netherview.message.MessageUtils;
13 | import me.gorgeousone.netherview.portal.Portal;
14 | import me.gorgeousone.netherview.portal.PortalLocator;
15 | import org.bukkit.Bukkit;
16 | import org.bukkit.GameMode;
17 | import org.bukkit.Location;
18 | import org.bukkit.block.Block;
19 | import org.bukkit.entity.Player;
20 | import org.bukkit.event.EventHandler;
21 | import org.bukkit.event.EventPriority;
22 | import org.bukkit.event.Listener;
23 | import org.bukkit.event.player.PlayerTeleportEvent;
24 |
25 | /**
26 | * Takes care of registering and linking nether portals when players use them.
27 | */
28 | public class PlayerTeleportListener implements Listener {
29 |
30 | private final ConfigSettings configSettings;
31 | private final PortalHandler portalHandler;
32 | private final ViewHandler viewHandler;
33 |
34 | public PlayerTeleportListener(
35 | ConfigSettings configSettings, PortalHandler portalHandler,
36 | ViewHandler viewHandler) {
37 |
38 | this.configSettings = configSettings;
39 | this.portalHandler = portalHandler;
40 | this.viewHandler = viewHandler;
41 | }
42 |
43 | //I did not use the PlayerPortalEvent because it only give information about where the player should theoretically perfectly teleport to
44 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
45 | public void onTeleport(PlayerTeleportEvent event) {
46 |
47 | Player player = event.getPlayer();
48 | Location from = event.getFrom();
49 | Location to = event.getTo();
50 |
51 | if (to == null) {
52 | return;
53 | }
54 |
55 | //updates portal animation for the player if they teleport with e.g. an ender pearl
56 | if (from.getWorld() == to.getWorld()) {
57 | viewHandler.hidePortalProjection(player);
58 | return;
59 | }
60 |
61 | if (event.getCause() != PlayerTeleportEvent.TeleportCause.NETHER_PORTAL ||
62 | !player.hasPermission(NetherViewPlugin.LINK_PERM)) {
63 | return;
64 | }
65 |
66 | viewHandler.unregisterPortalProjection(player);
67 | boolean createdNewPortalLink = createPortalLink(event);
68 |
69 | if (createdNewPortalLink && viewHandler.hasPortalViewEnabled(player) &&
70 | (player.getGameMode() == GameMode.CREATIVE || configSettings.cancelTeleportWhenLinkingPortalsEnabled())) {
71 | event.setCancelled(true);
72 | }
73 | }
74 |
75 | /**
76 | * Tries to locates or register portals at the given locations and to link them if they aren't already.
77 | */
78 | private boolean createPortalLink(PlayerTeleportEvent event) {
79 |
80 | Player player = event.getPlayer();
81 | Location from = event.getFrom();
82 | Location to = event.getTo();
83 |
84 | if (!configSettings.canCreatePortalViews(from.getWorld())) {
85 | MessageUtils.printDebug("World '" + from.getWorld().getName() + "' not listed in config for portal viewing");
86 | return false;
87 | }
88 |
89 | Block portalBlock = PortalLocator.getNearbyPortalBlock(from);
90 |
91 | //might happen if the player mysteriously moved more than a block away from the portal in split seconds
92 | if (portalBlock == null) {
93 | MessageUtils.printDebug("No portal found to teleport from at location [" + from.getWorld().getName() + "," + new BlockVec(from).toString() + "]");
94 | return false;
95 | }
96 |
97 | Block counterPortalBlock = PortalLocator.getNearbyPortalBlock(to);
98 |
99 | if (counterPortalBlock == null) {
100 | MessageUtils.printDebug("No portal found to teleport to at location [" + to.getWorld().getName() + "," + new BlockVec(to).toString() + "]");
101 | return false;
102 | }
103 |
104 | try {
105 | Portal portal = portalHandler.getPortalAt(portalBlock);
106 | Portal counterPortal = portalHandler.getPortalAt(counterPortalBlock);
107 |
108 | if (counterPortal.equals(portal.getCounterPortal())) {
109 | return false;
110 | }
111 |
112 | if (portal.isLinked()) {
113 |
114 | Bukkit.getPluginManager().callEvent(new PortalUnlinkEvent(portal, portal.getCounterPortal(), UnlinkReason.SWITCHED_TARGET_PORTAL));
115 | portal.removeLink();
116 |
117 | portalHandler.linkPortalTo(portal, counterPortal, player);
118 | return false;
119 | }
120 |
121 | portalHandler.linkPortalTo(portal, counterPortal, player);
122 | MessageUtils.sendInfo(player, Message.SUCCESSFUL_PORTAL_LINKING);
123 | return true;
124 |
125 | } catch (MessageException e) {
126 |
127 | MessageUtils.sendWarning(player, e.getPlayerMessage(), e.getPlaceholderValues());
128 | return false;
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/message/Message.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.message;
2 |
3 | import org.bukkit.ChatColor;
4 | import org.bukkit.configuration.file.FileConfiguration;
5 |
6 | public enum Message {
7 |
8 | SUCCESSFUL_PORTAL_LINKING("successful-portal-linking"),
9 | UNEQUAL_PORTALS("unequal-portal-sizes"),
10 | PORTAL_FRAME_INCOMPLETE("portal-frame-incomplete", "world-type"),
11 | PORTAL_CORNERS_INCOMPLETE("portal-corners-incomplete", "world-type"),
12 | PORTAL_TOO_BIG("portal-too-big", "size"),
13 | PORTAL_NOT_INTACT("portal-not-intact", "world-type"),
14 | WORLD_NOT_WHITE_LISTED("world-not-white-listed", "world"),
15 | NO_WORLD_FOUND("no-world-found", "world"),
16 | NO_PORTALS_FOUND("no-portals-found", "world"),
17 | NO_PORTAL_FOUND_NEARBY("no-portal-found-nearby"),
18 | PORTAL_INFO("portal-info", "location", "info"),
19 | WORLD_INFO("world-info", "count", "world", "portals"),
20 | FLIPPED_PORTAL("flipped-portal", "portal"),
21 | PORTAL_VIEWING_ON("portal-viewing-on"),
22 | PORTAL_VIEWING_OFF("portal-viewing-off"),
23 |
24 | WAND_INFO("wand-info"),
25 | SELECTION_INCOMPLETE("selection-incomplete"),
26 | SET_FIRST_CUBOID_POSITION("set-first-position", "position"),
27 | SET_SECOND_CUBOID_POSITION("set-second-position", "position"),
28 | SELECTION_TOO_SMALL("selection-too-small"),
29 | SELECTION_NOT_FLAT("selection-not-flat"),
30 | NO_PORTAL_FOUND_WITH_NAME("no-portal-found-with-name", "name"),
31 | PORTALS_INTERSECT("portals-intersect"),
32 | PORTAL_NAME_NOT_VALID("portal-name-not-valid"),
33 | PORTAL_NAME_NOT_UNIQUE("portal-name-not-unique", "name"),
34 | CREATED_PORTAL("created-portal", "name", "size"),
35 | REMOVED_PORTAL("deleted-portal", "name"),
36 | LINKED_PORTAL("linked-portal", "from-portal", "to-portal"),
37 | UNLINKED_PORTAL("unlinked-portal", "portal");
38 |
39 | private final String configKey;
40 | private String configValue;
41 | private final String[] placeholdersTokens;
42 |
43 | Message(String configKey, String... placeholdersTokens) {
44 | this.configKey = configKey;
45 | this.placeholdersTokens = placeholdersTokens;
46 | }
47 |
48 | private String getConfigKey() {
49 | return configKey;
50 | }
51 |
52 | public String[] getPlaceholdersTokens() {
53 | return placeholdersTokens;
54 | }
55 |
56 | public String[] getMessage(String... placeholderValues) {
57 |
58 | if (placeholderValues.length != placeholdersTokens.length) {
59 | throw new IllegalArgumentException("Expected " + placeholdersTokens.length + " placeholder values, found " + placeholderValues.length);
60 | }
61 |
62 | String formattedMessage = configValue;
63 |
64 | for (int i = 0; i < placeholdersTokens.length; ++i) {
65 | formattedMessage = formattedMessage.replace("%" + placeholdersTokens[i] + "%", placeholderValues[i]);
66 | }
67 |
68 | return ChatColor.translateAlternateColorCodes('&', formattedMessage).split("\\\\n");
69 | }
70 |
71 | public static void loadLangConfigValues(FileConfiguration langConfig) {
72 |
73 | for (Message message : Message.values()) {
74 |
75 | String configKey = message.getConfigKey();
76 |
77 | if (!langConfig.contains(configKey)) {
78 | throw new IllegalArgumentException("Language config does not contain key '" + configKey + "' for message " + message + ".");
79 | }
80 |
81 | message.configValue = langConfig.getString(configKey);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/message/MessageException.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.message;
2 |
3 | import java.util.Arrays;
4 |
5 | public class MessageException extends Exception {
6 |
7 | private final Message message;
8 | private final String[] placeholderValues;
9 |
10 | public MessageException(Message message, String... placeholderValues) {
11 | super(Arrays.toString(message.getMessage(placeholderValues)));
12 | this.message = message;
13 | this.placeholderValues = placeholderValues;
14 | }
15 |
16 | public Message getPlayerMessage() {
17 | return message;
18 | }
19 |
20 | public String[] getPlaceholderValues() {
21 | return placeholderValues;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/message/MessageUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.message;
2 |
3 | import net.md_5.bungee.api.chat.BaseComponent;
4 | import org.bukkit.Bukkit;
5 | import org.bukkit.ChatColor;
6 | import org.bukkit.command.CommandSender;
7 | import org.bukkit.entity.Player;
8 |
9 | public final class MessageUtils {
10 |
11 | private MessageUtils() {}
12 |
13 | public static boolean debugMessagesEnabled;
14 | public static boolean warningMessagesEnabled;
15 |
16 | public static void setWarningMessagesEnabled(boolean warningMessagesEnabled) {
17 | MessageUtils.warningMessagesEnabled = warningMessagesEnabled;
18 | }
19 |
20 | public static void setDebugMessagesEnabled(boolean debugMessagesEnabled) {
21 | MessageUtils.debugMessagesEnabled = debugMessagesEnabled;
22 | }
23 |
24 | public static void sendInfo(CommandSender sender, Message message, String... placeholderValues) {
25 | sender.sendMessage(message.getMessage(placeholderValues));
26 | }
27 |
28 | public static void sendWarning(CommandSender sender, Message message, String... placeholderValues) {
29 |
30 | if (warningMessagesEnabled) {
31 | sender.sendMessage(message.getMessage(placeholderValues));
32 | }
33 | }
34 |
35 | public static void printDebug(String message) {
36 |
37 | if (debugMessagesEnabled) {
38 | Bukkit.getConsoleSender().sendMessage(ChatColor.DARK_GRAY + "[Debug] " + message);
39 | }
40 | }
41 |
42 | public static void sendStaffInfo(String message) {
43 |
44 | for (Player player : Bukkit.getOnlinePlayers()) {
45 |
46 | if (player.isOp()) {
47 | player.sendMessage(message);
48 | }
49 | }
50 |
51 | Bukkit.getConsoleSender().sendMessage(message);
52 | }
53 |
54 | public static void sendStaffInfo(BaseComponent[] message) {
55 |
56 | for (Player player : Bukkit.getOnlinePlayers()) {
57 |
58 | if (player.isOp()) {
59 | player.spigot().sendMessage(message);
60 | }
61 | }
62 |
63 | Bukkit.getConsoleSender().spigot().sendMessage(message);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/packet/EntitySpawnPacketFactory1_13.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.packet;
2 |
3 | import com.comphenix.protocol.PacketType;
4 | import com.comphenix.protocol.ProtocolLibrary;
5 | import com.comphenix.protocol.events.PacketContainer;
6 | import com.comphenix.protocol.injector.PacketConstructor;
7 | import me.gorgeousone.netherview.utils.NmsUtils;
8 | import me.gorgeousone.netherview.utils.VersionUtils;
9 | import org.bukkit.entity.Entity;
10 |
11 | import java.lang.reflect.Constructor;
12 | import java.lang.reflect.Field;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.lang.reflect.Method;
15 |
16 | public final class EntitySpawnPacketFactory1_13 {
17 |
18 | private EntitySpawnPacketFactory1_13() {}
19 |
20 | private static Constructor> CONSTRUCTOR_TRACKER_ENTRY;
21 | private static Method TRACKER_ENTRY_CREATE_PACKET;
22 |
23 | private static Field NMS_SPAWN_PACKET_ENTITY_ID;
24 | private static Field NMS_SPAWN_PACKET_ENTITY_DATA;
25 |
26 | private static PacketConstructor CONSTRUCTOR_SPAWN_PACKET;
27 |
28 | static {
29 |
30 | if (!VersionUtils.serverIsAtOrAbove("1.14")) {
31 |
32 | try {
33 |
34 | Class> trackerEntryClass = NmsUtils.getNmsClass("EntityTrackerEntry");
35 | CONSTRUCTOR_TRACKER_ENTRY = trackerEntryClass.getConstructor(NmsUtils.getNmsClass("Entity"), int.class, int.class, int.class, boolean.class);
36 | TRACKER_ENTRY_CREATE_PACKET = trackerEntryClass.getDeclaredMethod("e");
37 |
38 | boolean serverIs1_8 = !VersionUtils.serverIsAtOrAbove("1.9");
39 |
40 | Class> entitySpawnPacket = NmsUtils.getNmsClass("PacketPlayOutSpawnEntity");
41 | NMS_SPAWN_PACKET_ENTITY_ID = entitySpawnPacket.getDeclaredField(serverIs1_8 ? "j" : "k");
42 | NMS_SPAWN_PACKET_ENTITY_DATA = entitySpawnPacket.getDeclaredField(serverIs1_8 ? "k" : "l");
43 |
44 | CONSTRUCTOR_SPAWN_PACKET = ProtocolLibrary.getProtocolManager().createPacketConstructor(PacketType.Play.Server.SPAWN_ENTITY, NmsUtils.getNmsClass("Entity"), int.class, int.class);
45 |
46 | } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) {
47 | e.printStackTrace();
48 | }
49 | }
50 | }
51 |
52 | /**
53 | * Creates a spawn packet container for <1.14 non living entities.
54 | * It first creates a nms spawn packet with a method from EntityTrackerEntry in order to
55 | * read entity object data values from it (which are difficult to replicate by hand).
56 | */
57 | public static PacketContainer createPacket(Entity entity) {
58 |
59 | try {
60 | TRACKER_ENTRY_CREATE_PACKET.setAccessible(true);
61 | NMS_SPAWN_PACKET_ENTITY_ID.setAccessible(true);
62 | NMS_SPAWN_PACKET_ENTITY_DATA.setAccessible(true);
63 |
64 | Object trackerEntry = CONSTRUCTOR_TRACKER_ENTRY.newInstance(NmsUtils.getHandle(entity), 0, 0, 0, false);
65 | Object nmsSpawnPacket = TRACKER_ENTRY_CREATE_PACKET.invoke(trackerEntry);
66 |
67 | int entityId = NMS_SPAWN_PACKET_ENTITY_ID.getInt(nmsSpawnPacket);
68 | int entityData = NMS_SPAWN_PACKET_ENTITY_DATA.getInt(nmsSpawnPacket);
69 |
70 | TRACKER_ENTRY_CREATE_PACKET.setAccessible(false);
71 | NMS_SPAWN_PACKET_ENTITY_ID.setAccessible(false);
72 | NMS_SPAWN_PACKET_ENTITY_DATA.setAccessible(false);
73 |
74 | return CONSTRUCTOR_SPAWN_PACKET.createPacket(NmsUtils.getHandle(entity), entityId, entityData);
75 |
76 | } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
77 |
78 | e.printStackTrace();
79 | return null;
80 | }
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/portal/Portal.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.portal;
2 |
3 | import me.gorgeousone.netherview.blockcache.BlockCache;
4 | import me.gorgeousone.netherview.blockcache.ProjectionCache;
5 | import me.gorgeousone.netherview.blockcache.Transform;
6 | import me.gorgeousone.netherview.geometry.AxisAlignedRect;
7 | import me.gorgeousone.netherview.geometry.BlockVec;
8 | import me.gorgeousone.netherview.geometry.Cuboid;
9 | import me.gorgeousone.netherview.wrapper.Axis;
10 | import org.bukkit.Location;
11 | import org.bukkit.World;
12 | import org.bukkit.block.Block;
13 |
14 | import java.util.HashSet;
15 | import java.util.Map;
16 | import java.util.Objects;
17 | import java.util.Set;
18 |
19 | /**
20 | * A class containing information about a located portal structure in a world.
21 | */
22 | public class Portal {
23 |
24 | private final World world;
25 | private final AxisAlignedRect portalRect;
26 |
27 | private final Set portalBlocks;
28 |
29 | private Portal counterPortal;
30 | private Transform tpTransform;
31 |
32 | private Map.Entry blockCaches;
33 | private Map.Entry projectionCaches;
34 |
35 | private final Cuboid frameShape;
36 | private final Cuboid innerShape;
37 |
38 | private boolean isViewFlipped;
39 |
40 | public Portal(World world,
41 | AxisAlignedRect portalRect,
42 | Cuboid frameShape,
43 | Cuboid innerShape,
44 | Set portalBlocks) {
45 |
46 | this.world = world;
47 | this.portalRect = portalRect.clone();
48 | this.frameShape = frameShape.clone();
49 | this.innerShape = innerShape.clone();
50 | this.portalBlocks = portalBlocks;
51 | }
52 |
53 | public World getWorld() {
54 | return world;
55 | }
56 |
57 | public Location getLocation() {
58 | return portalRect.getMin().toLocation(world);
59 | }
60 |
61 | public BlockVec getMaxBlockAtFloor() {
62 |
63 | BlockVec maxBlock = frameShape.getMax().clone().add(-1, 0, -1);
64 | maxBlock.setY(frameShape.getMin().getY());
65 | return maxBlock;
66 | }
67 |
68 | public Cuboid getFrame() {
69 | return frameShape;
70 | }
71 |
72 | public Cuboid getInner() {
73 | return innerShape;
74 | }
75 |
76 | public int width() {
77 | return getAxis() == Axis.X ? frameShape.getWidthX() : frameShape.getWidthZ();
78 | }
79 |
80 | public int height() {
81 | return frameShape.getHeight();
82 | }
83 |
84 | public AxisAlignedRect getPortalRect() {
85 | return portalRect.clone();
86 | }
87 |
88 | public Axis getAxis() {
89 | return portalRect.getAxis();
90 | }
91 |
92 | public Set getPortalBlocks() {
93 | return new HashSet<>(portalBlocks);
94 | }
95 |
96 | public boolean equalsInSize(Portal other) {
97 |
98 | AxisAlignedRect otherRect = other.getPortalRect();
99 |
100 | return portalRect.width() == otherRect.width() &&
101 | portalRect.height() == otherRect.height();
102 | }
103 |
104 | public Portal getCounterPortal() {
105 | return counterPortal;
106 | }
107 |
108 | public void setTpTransform(Transform tpTransform) {
109 | this.tpTransform = tpTransform;
110 | }
111 |
112 | public Transform getTpTransform() {
113 | return tpTransform;
114 | }
115 |
116 | public void setLinkedTo(Portal counterPortal) {
117 | this.counterPortal = counterPortal;
118 | }
119 |
120 | public void removeLink() {
121 |
122 | this.counterPortal = null;
123 | this.tpTransform = null;
124 | removeProjectionCaches();
125 | }
126 |
127 | public boolean isLinked() {
128 | return counterPortal != null;
129 | }
130 |
131 | public void setBlockCaches(Map.Entry blockCaches) {
132 | this.blockCaches = blockCaches;
133 | }
134 |
135 | public void removeBlockCaches() {
136 | blockCaches = null;
137 | }
138 |
139 | public boolean blockCachesAreLoaded() {
140 | return blockCaches != null;
141 | }
142 |
143 | public BlockCache getFrontCache() {
144 | return blockCaches.getKey();
145 | }
146 |
147 | public BlockCache getBackCache() {
148 | return blockCaches.getValue();
149 | }
150 |
151 | /**
152 | * Sets the front and back projection caches for this portal.
153 | *
154 | * @param projectionCaches where the key is referred to as front projection and value as back projection
155 | */
156 | public void setProjectionCaches(Map.Entry projectionCaches) {
157 | this.projectionCaches = projectionCaches;
158 | }
159 |
160 | public void removeProjectionCaches() {
161 | this.projectionCaches = null;
162 | }
163 |
164 | public boolean projectionsAreLoaded() {
165 | return projectionCaches != null;
166 | }
167 |
168 | public ProjectionCache getFrontProjection() {
169 | return projectionCaches.getKey();
170 | }
171 |
172 | public ProjectionCache getBackProjection() {
173 | return projectionCaches.getValue();
174 | }
175 |
176 | /**
177 | * Returns true if the the 2 projections of the portal have been switched with each other for aesthetic reasons.
178 | */
179 | public boolean isViewFlipped() {
180 | return isViewFlipped;
181 | }
182 |
183 | /**
184 | * Sets whether the 2 projections of the portal are switched with each other or not.
185 | * The {@link ProjectionCache}s have to be set again to realize this change.
186 | */
187 | public void setViewFlipped(boolean isViewFlipped) {
188 | this.isViewFlipped = isViewFlipped;
189 | }
190 |
191 | public void flipView() {
192 | isViewFlipped = !isViewFlipped;
193 | }
194 |
195 | @Override
196 | public String toString() {
197 | return BlockVec.toSimpleString(getLocation());
198 | }
199 |
200 | @Override
201 | public int hashCode() {
202 | return Objects.hash(getLocation());
203 | }
204 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/portal/PortalLocator.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.portal;
2 |
3 | import me.gorgeousone.netherview.geometry.AxisAlignedRect;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.geometry.Cuboid;
6 | import me.gorgeousone.netherview.message.Message;
7 | import me.gorgeousone.netherview.message.MessageException;
8 | import me.gorgeousone.netherview.message.MessageUtils;
9 | import me.gorgeousone.netherview.utils.FacingUtils;
10 | import me.gorgeousone.netherview.wrapper.Axis;
11 | import org.bukkit.Location;
12 | import org.bukkit.Material;
13 | import org.bukkit.World;
14 | import org.bukkit.block.Block;
15 | import org.bukkit.block.BlockFace;
16 | import org.bukkit.util.Vector;
17 |
18 | import java.util.HashSet;
19 | import java.util.Set;
20 |
21 | public class PortalLocator {
22 |
23 | private static Material PORTAL_MATERIAL;
24 | private static int MAX_PORTAL_SIZE;
25 |
26 | public static void configureVersion(Material portalMaterial) {
27 | PortalLocator.PORTAL_MATERIAL = portalMaterial;
28 | }
29 |
30 | public static void setMaxPortalSize(int portalSize) {
31 | MAX_PORTAL_SIZE = portalSize;
32 | }
33 |
34 | /**
35 | * Finds the portal block a player might have touched at the location or the blocks next to it
36 | * (players in creative mode often teleport to the nether before their location appears to be inside a portal).
37 | */
38 | public static Block getNearbyPortalBlock(Location location) {
39 |
40 | Block block = location.getBlock();
41 |
42 | if (block.getType() == PORTAL_MATERIAL) {
43 | return block;
44 | }
45 |
46 | for (BlockFace face : FacingUtils.getAxesFaces()) {
47 | Block neighbor = block.getRelative(face);
48 |
49 | if (neighbor.getType() == PORTAL_MATERIAL) {
50 | return neighbor;
51 | }
52 | }
53 |
54 | return null;
55 | }
56 |
57 | public static Portal locatePortalStructure(Block portalBlock) throws MessageException {
58 |
59 | //this only happens when some data read from the portal config is wrong
60 | //that's why it is not a gray message
61 | if (portalBlock.getType() != PORTAL_MATERIAL) {
62 | throw new IllegalStateException("No portal found at " + BlockVec.toSimpleString(portalBlock.getLocation()));
63 | }
64 |
65 | World world = portalBlock.getWorld();
66 | AxisAlignedRect portalRect = getPortalRect(portalBlock);
67 | Axis portalAxis = portalRect.getAxis();
68 |
69 | BlockVec portalMin = new BlockVec(portalRect.getMin());
70 | BlockVec portalMax = new BlockVec(portalRect.getMax());
71 | portalMax.add(new BlockVec(portalAxis.getNormal()));
72 |
73 | Cuboid innerShape = new Cuboid(portalMin, portalMax);
74 | Set innerBlocks = getInnerPortalBlocks(world, innerShape);
75 | checkInnerBlocksConsistency(innerBlocks);
76 |
77 | BlockVec frameExtent = new BlockVec(portalAxis.getCrossNormal()).setY(1);
78 | Cuboid frameShape = new Cuboid(portalMin.subtract(frameExtent), portalMax.add(frameExtent));
79 | checkFrameBlocksOcclusion(world, frameShape, portalRect.getAxis());
80 |
81 | return new Portal(world, portalRect, frameShape, innerShape, innerBlocks);
82 | }
83 |
84 | /**
85 | * Returns a rectangle with the size and location of the rectangle the inner portal blocks form.
86 | */
87 | private static AxisAlignedRect getPortalRect(Block portalBlock) throws MessageException {
88 |
89 | Axis portalAxis = FacingUtils.getAxis(portalBlock);
90 |
91 | Vector rectMin = new Vector(
92 | portalBlock.getX(),
93 | getPortalExtent(portalBlock, BlockFace.DOWN).getY(),
94 | portalBlock.getZ());
95 |
96 | Vector rectMax = rectMin.clone();
97 | rectMax.setY(getPortalExtent(portalBlock, BlockFace.UP).getY() + 1);
98 |
99 | if (portalAxis == Axis.X) {
100 | rectMin.setX(getPortalExtent(portalBlock, BlockFace.WEST).getX());
101 | rectMax.setX(getPortalExtent(portalBlock, BlockFace.EAST).getX() + 1);
102 |
103 | } else {
104 | rectMin.setZ(getPortalExtent(portalBlock, BlockFace.NORTH).getZ());
105 | rectMax.setZ(getPortalExtent(portalBlock, BlockFace.SOUTH).getZ() + 1);
106 | }
107 |
108 | //translate the portalRect towards the middle of the block;
109 | AxisAlignedRect portalRect = new AxisAlignedRect(portalAxis, rectMin, rectMax);
110 |
111 | if (portalRect.width() > MAX_PORTAL_SIZE || portalRect.height() > MAX_PORTAL_SIZE) {
112 | throw new MessageException(Message.PORTAL_TOO_BIG, String.valueOf(MAX_PORTAL_SIZE));
113 | }
114 |
115 | portalRect.translate(portalRect.getPlane().getNormal().multiply(0.5));
116 | return portalRect;
117 | }
118 |
119 | /**
120 | * Returns the last portal block of a portal's extent into a certain direction.
121 | */
122 | private static Block getPortalExtent(Block sourceBlock, BlockFace facing) throws MessageException {
123 |
124 | Block blockIterator = sourceBlock;
125 |
126 | for (int i = 0; i <= MAX_PORTAL_SIZE; i++) {
127 |
128 | Block nextBlock = blockIterator.getRelative(facing);
129 |
130 | if (nextBlock.getType() != PORTAL_MATERIAL) {
131 | return blockIterator;
132 | }
133 |
134 | blockIterator = nextBlock;
135 | }
136 |
137 | MessageUtils.printDebug("Detection stopped after exceeding " + MAX_PORTAL_SIZE + " portal blocks towards " + facing.name() + " at " + BlockVec.toSimpleString(blockIterator.getLocation()));
138 | throw new MessageException(Message.PORTAL_TOO_BIG, String.valueOf(MAX_PORTAL_SIZE));
139 | }
140 |
141 | /**
142 | * Returns a set of blocks of all portal blocks of a portal according to the passed rectangle.
143 | */
144 | private static Set getInnerPortalBlocks(World world,
145 | Cuboid portalInner) throws MessageException {
146 |
147 | BlockVec portalMin = portalInner.getMin();
148 | BlockVec portalMax = portalInner.getMax();
149 | Set portalBlocks = new HashSet<>();
150 |
151 | for (int x = portalMin.getX(); x < portalMax.getX(); x++) {
152 | for (int y = portalMin.getY(); y < portalMax.getY(); y++) {
153 | for (int z = portalMin.getZ(); z < portalMax.getZ(); z++) {
154 |
155 | Block portalBlock = world.getBlockAt(x, y, z);
156 |
157 | if (portalBlock.getType() == PORTAL_MATERIAL) {
158 | portalBlocks.add(portalBlock);
159 |
160 | } else {
161 |
162 | MessageUtils.printDebug("Expected portal block at " + BlockVec.toSimpleString(portalBlock.getLocation()));
163 | String worldType = world.getEnvironment().name().toLowerCase().replaceAll("_", " ");
164 | throw new MessageException(Message.PORTAL_NOT_INTACT, worldType);
165 | }
166 | }
167 | }
168 | }
169 |
170 | return portalBlocks;
171 | }
172 |
173 | private static void checkInnerBlocksConsistency(Set portalBlocks) throws MessageException {
174 |
175 | for (Block portalBlock : portalBlocks) {
176 |
177 | if (portalBlock.getType() != PORTAL_MATERIAL) {
178 |
179 | MessageUtils.printDebug("Expected portal block at " + BlockVec.toSimpleString(portalBlock.getLocation()));
180 | String worldType = portalBlock.getWorld().getEnvironment().name().toLowerCase().replaceAll("_", " ");
181 | throw new MessageException(Message.PORTAL_NOT_INTACT, worldType);
182 | }
183 | }
184 | }
185 |
186 | /**
187 | * Returns a set of blocks where obsidian needs to be placed for a portal frame according to the given portal bounds.
188 | */
189 | private static void checkFrameBlocksOcclusion(World world,
190 | Cuboid portalFrame,
191 | Axis portalAxis) throws MessageException {
192 |
193 | BlockVec portalMin = portalFrame.getMin();
194 | BlockVec portalMax = portalFrame.getMax();
195 |
196 | for (int x = portalMin.getX(); x < portalMax.getX(); x++) {
197 | for (int y = portalMin.getY(); y < portalMax.getY(); y++) {
198 | for (int z = portalMin.getZ(); z < portalMax.getZ(); z++) {
199 |
200 | //only check for obsidian frame blocks that are part of the portal frame
201 | if (y > portalMin.getY() && y < portalMax.getY() - 1 &&
202 | (portalAxis == Axis.X ?
203 | x > portalMin.getX() && x < portalMax.getX() - 1 :
204 | z > portalMin.getZ() && z < portalMax.getZ() - 1)) {
205 | continue;
206 | }
207 |
208 | Block portalBlock = world.getBlockAt(x, y, z);
209 | Material portalBlockType = portalBlock.getType();
210 |
211 | if (portalBlockType.isOccluding()) {
212 | continue;
213 | }
214 |
215 | MessageUtils.printDebug("Expected obsidian/occluding block at portal corner block " + BlockVec.toSimpleString(portalBlock.getLocation()));
216 |
217 | String worldType = world.getEnvironment().name().toLowerCase().replaceAll("_", " ");
218 |
219 | if (isPortalCorner(x, y, z, portalMin, portalMax, portalAxis)) {
220 | throw new MessageException(Message.PORTAL_CORNERS_INCOMPLETE, worldType);
221 |
222 | } else {
223 | throw new MessageException(Message.PORTAL_FRAME_INCOMPLETE, worldType);
224 | }
225 | }
226 | }
227 | }
228 | }
229 |
230 | private static boolean isPortalCorner(int x,
231 | int y,
232 | int z,
233 | BlockVec portalMin,
234 | BlockVec portalMax,
235 | Axis portalAxis) {
236 |
237 | if (y != portalMin.getY() && y != portalMax.getY() - 1) {
238 | return false;
239 | }
240 |
241 | if (portalAxis == Axis.X) {
242 | return (x == portalMin.getX() || x == portalMax.getX() - 1);
243 | } else {
244 | return (z == portalMin.getZ() || z == portalMax.getZ() - 1);
245 | }
246 | }
247 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/portal/PortalSerializer.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.portal;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.customportal.CustomPortal;
5 | import me.gorgeousone.netherview.geometry.BlockVec;
6 | import me.gorgeousone.netherview.handlers.PortalHandler;
7 | import me.gorgeousone.netherview.message.MessageException;
8 | import me.gorgeousone.netherview.utils.VersionUtils;
9 | import org.bukkit.Bukkit;
10 | import org.bukkit.World;
11 | import org.bukkit.configuration.ConfigurationSection;
12 | import org.bukkit.configuration.file.FileConfiguration;
13 | import org.bukkit.plugin.java.JavaPlugin;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.Set;
18 | import java.util.UUID;
19 |
20 | public class PortalSerializer {
21 |
22 | private final JavaPlugin plugin;
23 | private final ConfigSettings configSettings;
24 | private final PortalHandler portalHandler;
25 |
26 | public PortalSerializer(JavaPlugin plugin,
27 | ConfigSettings configSettings,
28 | PortalHandler portalHandler) {
29 |
30 | this.configSettings = configSettings;
31 | this.portalHandler = portalHandler;
32 | this.plugin = plugin;
33 | }
34 |
35 | public void savePortals(FileConfiguration portalConfig) {
36 |
37 | portalConfig.set("plugin-version", plugin.getDescription().getVersion());
38 | portalConfig.set("portals", null);
39 |
40 | ConfigurationSection portalSection = portalConfig.createSection("portals");
41 |
42 | for (World world : Bukkit.getWorlds()) {
43 |
44 | if (!portalHandler.hasPortals(world)) {
45 | continue;
46 | }
47 |
48 | Set portalsInWorld = portalHandler.getPortals(world);
49 | String worldId = world.getUID().toString();
50 | ConfigurationSection worldSection = null;
51 |
52 | for (Portal portal : portalsInWorld) {
53 |
54 | if (portal instanceof CustomPortal) {
55 | continue;
56 | }
57 |
58 | if (worldSection == null) {
59 | worldSection = portalSection.createSection(worldId);
60 | }
61 |
62 | int portalHash = portal.hashCode();
63 | ConfigurationSection portalData = worldSection.createSection(Integer.toString(portalHash));
64 |
65 | portalData.set("location", new BlockVec(portal.getLocation()).serialize());
66 | portalData.set("is-flipped", portal.isViewFlipped());
67 |
68 | if (portal.isLinked()) {
69 | portalData.set("link", portal.getCounterPortal().hashCode());
70 | }
71 | }
72 | }
73 | }
74 |
75 | public void loadPortals(FileConfiguration portalConfig) {
76 |
77 | if (!portalConfig.contains("plugin-version") || VersionUtils.isVersionLowerThan(portalConfig.getString("plugin-version"), "3")) {
78 | new PortalSerializer2_1_0(plugin, configSettings, portalHandler).loadPortals(portalConfig);
79 | return;
80 | }
81 |
82 | if (!portalConfig.contains("portals")) {
83 | return;
84 | }
85 |
86 | ConfigurationSection portalsSection = portalConfig.getConfigurationSection("portals");
87 | Map portalLinks = new HashMap<>();
88 |
89 | for (String worldId : portalsSection.getKeys(false)) {
90 |
91 | World world = Bukkit.getWorld(UUID.fromString(worldId));
92 |
93 | if (world == null) {
94 | plugin.getLogger().warning("Could not find world with ID: '" + worldId + "'. Portals saved for this world will not be loaded.");
95 | continue;
96 | }
97 |
98 | if (configSettings.canCreatePortalViews(world)) {
99 |
100 | ConfigurationSection worldSection = portalsSection.getConfigurationSection(worldId);
101 |
102 | for (String portalHashString : worldSection.getKeys(false)) {
103 | deserializePortal(world, portalHashString, worldSection.getConfigurationSection(portalHashString), portalLinks);
104 | }
105 | }
106 | }
107 |
108 | linkPortals(portalLinks);
109 | }
110 |
111 | private void deserializePortal(World world,
112 | String portalHashString,
113 | ConfigurationSection portalData,
114 | Map portalLinks) {
115 |
116 | try {
117 |
118 | int portalHash = Integer.parseInt(portalHashString);
119 |
120 | BlockVec portalLoc = BlockVec.fromString(portalData.getString("location"));
121 | Portal portal = PortalLocator.locatePortalStructure(portalLoc.toBlock(world));
122 | portalHandler.addPortal(portal);
123 | portal.setViewFlipped(portalData.getBoolean("is-flipped"));
124 |
125 | if (portalData.contains("link")) {
126 | portalLinks.put(portalHash, portalData.getInt("link"));
127 | }
128 |
129 | } catch (IllegalArgumentException | IllegalStateException | MessageException e) {
130 | plugin.getLogger().warning("Unable to load portal '" + portalHashString + "' in world " + world.getName() + "': " + e.getMessage());
131 | }
132 | }
133 |
134 | private void linkPortals(Map portalLinks) {
135 |
136 | for (Map.Entry entry : portalLinks.entrySet()) {
137 |
138 | Integer fromPortalHash = entry.getKey();
139 | Integer toPortalHash = entry.getValue();
140 |
141 | Portal fromPortal = portalHandler.getPortalByHash(fromPortalHash);
142 | Portal toPortal = portalHandler.getPortalByHash(toPortalHash);
143 |
144 | if (toPortal == null) {
145 | plugin.getLogger().warning("Could not find custom portal with name'" + toPortalHash + "' for linking with portal '" + fromPortalHash + "'.");
146 | continue;
147 | }
148 |
149 | try {
150 | portalHandler.linkPortalTo(fromPortal, toPortal, null);
151 |
152 | } catch (MessageException e) {
153 | plugin.getLogger().warning("Unable to link custom portal '" + fromPortalHash + "' to portal '" + toPortalHash + "': " + e.getMessage());
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/portal/PortalSerializer2_1_0.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.portal;
2 |
3 | import me.gorgeousone.netherview.ConfigSettings;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.handlers.PortalHandler;
6 | import me.gorgeousone.netherview.message.MessageException;
7 | import org.bukkit.Bukkit;
8 | import org.bukkit.World;
9 | import org.bukkit.configuration.ConfigurationSection;
10 | import org.bukkit.configuration.file.FileConfiguration;
11 | import org.bukkit.plugin.java.JavaPlugin;
12 |
13 | import java.util.List;
14 | import java.util.UUID;
15 |
16 | public class PortalSerializer2_1_0 {
17 |
18 | private final JavaPlugin plugin;
19 | private final ConfigSettings configSettings;
20 | private final PortalHandler portalHandler;
21 |
22 | public PortalSerializer2_1_0(JavaPlugin plugin,
23 | ConfigSettings configSettings,
24 | PortalHandler portalHandler) {
25 | this.configSettings = configSettings;
26 | this.portalHandler = portalHandler;
27 | this.plugin = plugin;
28 | }
29 |
30 | public void loadPortals(FileConfiguration portalConfig) {
31 |
32 | loadPortalLocations(portalConfig);
33 | loadPortalData(portalConfig);
34 | }
35 |
36 | private void loadPortalLocations(FileConfiguration portalConfig) {
37 |
38 | if (!portalConfig.contains("portal-locations")) {
39 | return;
40 | }
41 |
42 | ConfigurationSection portalLocations = portalConfig.getConfigurationSection("portal-locations");
43 |
44 | for (String worldID : portalLocations.getKeys(false)) {
45 |
46 | World worldWithPortals = Bukkit.getWorld(UUID.fromString(worldID));
47 |
48 | if (worldWithPortals == null) {
49 | plugin.getLogger().warning("Could not find world with ID: '" + worldID + "'. Portals saved for this world will not be loaded.");
50 | continue;
51 | }
52 |
53 | if (configSettings.canCreatePortalViews(worldWithPortals)) {
54 | deserializePortals(worldWithPortals, portalLocations.getStringList(worldID));
55 | }
56 | }
57 | }
58 |
59 | private void deserializePortals(World world, List portalLocs) {
60 |
61 | for (String serializedBlockVec : portalLocs) {
62 |
63 | try {
64 | BlockVec portalLoc = BlockVec.fromString(serializedBlockVec);
65 | Portal portal = PortalLocator.locatePortalStructure(portalLoc.toBlock(world));
66 | portalHandler.addPortal(portal);
67 |
68 | } catch (IllegalArgumentException | IllegalStateException | MessageException e) {
69 | plugin.getLogger().warning("Unable to load portal at [" + world.getName() + "," + serializedBlockVec + "]: " + e.getMessage());
70 | }
71 | }
72 | }
73 |
74 | private void loadPortalData(FileConfiguration portalConfig) {
75 |
76 | if (!portalConfig.contains("portal-data")) {
77 | return;
78 | }
79 |
80 | ConfigurationSection portalData = portalConfig.getConfigurationSection("portal-data");
81 |
82 | for (String portalHashString : portalData.getKeys(false)) {
83 |
84 | Portal portal = portalHandler.getPortalByHash(Integer.parseInt(portalHashString));
85 |
86 | if (portal == null) {
87 | continue;
88 | }
89 |
90 | portal.setViewFlipped(portalData.getBoolean(portalHashString + ".is-flipped"));
91 |
92 | if (!portalData.contains(portalHashString + ".link")) {
93 | continue;
94 | }
95 |
96 | int linkedPortalHash = portalData.getInt(portalHashString + ".link");
97 | Portal counterPortal = portalHandler.getPortalByHash(linkedPortalHash);
98 |
99 | if (counterPortal != null) {
100 |
101 | try {
102 | portalHandler.linkPortalTo(portal, counterPortal, null);
103 | } catch (MessageException e) {
104 | plugin.getLogger().warning("Could not link portal '" + portal.toString() + "' to portal '" + counterPortal.toString() + "': " + e.getMessage());
105 | }
106 | }
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/portal/ProjectionEntity.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.portal;
2 |
3 | import org.bukkit.Location;
4 | import org.bukkit.entity.Entity;
5 |
6 | import java.util.Random;
7 |
8 | public class ProjectionEntity {
9 |
10 | private final static Random RANDOM = new Random();
11 |
12 | private final Entity entity;
13 | private final int fakeId;
14 | private Location lastLoc;
15 |
16 | public ProjectionEntity(Entity entity) {
17 |
18 | this.entity = entity;
19 | //chances to match an existing id are like 10,000/2,000,000,000 which is 0,0005%. hope that's enough
20 | this.fakeId = RANDOM.nextInt();
21 | }
22 |
23 | public Entity getEntity() {
24 | return entity;
25 | }
26 |
27 | public int getFakeId() {
28 | return fakeId;
29 | }
30 |
31 | public Location getLastLoc() {
32 | return lastLoc;
33 | }
34 |
35 | public void updateLastLoc() {
36 | lastLoc = entity.getLocation();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/updatechecks/UpdateCheck.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.updatechecks;
2 |
3 | import me.gorgeousone.netherview.message.MessageUtils;
4 | import me.gorgeousone.netherview.utils.VersionUtils;
5 | import net.md_5.bungee.api.ChatColor;
6 | import net.md_5.bungee.api.chat.ClickEvent;
7 | import net.md_5.bungee.api.chat.ComponentBuilder;
8 | import net.md_5.bungee.api.chat.HoverEvent;
9 | import org.bukkit.Bukkit;
10 | import org.bukkit.plugin.java.JavaPlugin;
11 |
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.net.URL;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 | import java.util.Scanner;
18 |
19 | public class UpdateCheck {
20 |
21 | private final JavaPlugin plugin;
22 |
23 | private final String currentVersion;
24 | private final int resourceId;
25 | private final String resourceName;
26 | private final String updateInfoPasteUrl;
27 |
28 | public UpdateCheck(JavaPlugin plugin, int resourceId, String resourceName, String updateInfoPasteUrl) {
29 |
30 | this.plugin = plugin;
31 | this.currentVersion = plugin.getDescription().getVersion();
32 | this.resourceId = resourceId;
33 | this.resourceName = resourceName;
34 | this.updateInfoPasteUrl = updateInfoPasteUrl;
35 | }
36 |
37 | public void run(int maxDisplayedMessages) {
38 |
39 | Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
40 |
41 | try {
42 | List newUpdates = readNewUpdates();
43 |
44 | if (newUpdates.isEmpty()) {
45 | plugin.getLogger().info("Plugin is up to date :)");
46 | return;
47 | }
48 |
49 | if (newUpdates.size() < 2) {
50 | MessageUtils.sendStaffInfo("A new version of Nether View is available!");
51 | }else {
52 | MessageUtils.sendStaffInfo("New updates for Nether View are available!");
53 | }
54 |
55 | for (int i = 0; i < newUpdates.size(); ++i) {
56 |
57 | if (i > maxDisplayedMessages - 1) {
58 | ComponentBuilder message = new ComponentBuilder((newUpdates.size() - maxDisplayedMessages) + " more...").color(ChatColor.LIGHT_PURPLE);
59 | message.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/" + resourceName + "." + resourceId + "/updates"));
60 | message.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("see all updates").create()));
61 | MessageUtils.sendStaffInfo(message.create());
62 | break;
63 | }
64 |
65 | MessageUtils.sendStaffInfo(newUpdates.get(i).getChatMessage());
66 | }
67 |
68 | ComponentBuilder builder = new ComponentBuilder("download").color(ChatColor.YELLOW).underlined(true);
69 | builder.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/" + resourceName + "." + resourceId));
70 | builder.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("visit ").append("download page").color(ChatColor.LIGHT_PURPLE).create()));
71 | MessageUtils.sendStaffInfo(builder.create());
72 |
73 | } catch (IOException exception) {
74 | plugin.getLogger().info("Unable to check for updates...");
75 | }
76 | });
77 | }
78 |
79 | private List readNewUpdates() throws IOException {
80 |
81 | InputStream inputStream = new URL(updateInfoPasteUrl).openStream();
82 | Scanner scanner = new Scanner(inputStream);
83 | List updates = new ArrayList<>();
84 |
85 | while (scanner.hasNext()) {
86 |
87 | UpdateInfo updateInfo = new UpdateInfo(scanner.nextLine(), resourceName, resourceId);
88 |
89 | if (VersionUtils.isVersionLowerThan(currentVersion, updateInfo.getVersion())) {
90 | System.out.println(currentVersion + " is lower than " + updateInfo.getVersion());
91 | updates.add(updateInfo);
92 | }else {
93 | break;
94 | }
95 | }
96 | return updates;
97 | }
98 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/updatechecks/UpdateInfo.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.updatechecks;
2 |
3 | import net.md_5.bungee.api.ChatColor;
4 | import net.md_5.bungee.api.chat.BaseComponent;
5 | import net.md_5.bungee.api.chat.ClickEvent;
6 | import net.md_5.bungee.api.chat.ComponentBuilder;
7 | import net.md_5.bungee.api.chat.HoverEvent;
8 |
9 | public class UpdateInfo {
10 |
11 | private final String version;
12 | private final String description;
13 | private final String updateId;
14 |
15 | private final int resourceId;
16 | private final String resourceName;
17 |
18 | public UpdateInfo(String updateInfo, String resourceName, int resourceId) {
19 |
20 | String[] split = updateInfo.split(";");
21 | version = split[0];
22 | description = split[1];
23 | updateId = split[2];
24 |
25 | this.resourceName = resourceName;
26 | this.resourceId = resourceId;
27 | }
28 |
29 | public String getVersion() {
30 | return version;
31 | }
32 |
33 | public BaseComponent[] getChatMessage() {
34 |
35 | ComponentBuilder builder = new ComponentBuilder(version).color(ChatColor.LIGHT_PURPLE);
36 | builder.append(" " + description).color(ChatColor.YELLOW);
37 | builder.event(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.spigotmc.org/resources/" + resourceName + "." + resourceId + "/update?update=" + updateId));
38 | builder.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("read more about ").append(version).color(ChatColor.LIGHT_PURPLE).create()));
39 |
40 | return builder.create();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/updatechecks/VersionResponse.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.updatechecks;
2 |
3 | public enum VersionResponse {
4 | LATEST, FOUND_NEW, UNAVAILABLE
5 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/ConfigUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import org.bukkit.configuration.file.YamlConfiguration;
4 | import org.bukkit.plugin.java.JavaPlugin;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 |
10 | public final class ConfigUtils {
11 |
12 | private ConfigUtils() {}
13 |
14 | public static YamlConfiguration loadConfig(String configName, JavaPlugin plugin) {
15 |
16 | File configFile = new File(plugin.getDataFolder() + File.separator + configName + ".yml");
17 | YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(plugin.getResource(configName + ".yml")));
18 |
19 | if (!configFile.exists()) {
20 | plugin.saveResource(configName + ".yml", true);
21 | }
22 |
23 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
24 | config.setDefaults(defConfig);
25 | config.options().copyDefaults(true);
26 |
27 | try {
28 | config.save(configFile);
29 | } catch (IOException e) {
30 | e.printStackTrace();
31 | }
32 |
33 | return config;
34 | }
35 |
36 | // public static YamlConfiguration loadDefaultConfig(String configName, JavaPlugin plugin) {
37 | //
38 | // InputStream defConfigStream = plugin.getResource(configName + ".yml");
39 | // return YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream));
40 | // }
41 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/FacingUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import com.comphenix.protocol.wrappers.EnumWrappers;
4 | import me.gorgeousone.netherview.geometry.BlockVec;
5 | import me.gorgeousone.netherview.wrapper.Axis;
6 | import org.bukkit.block.Block;
7 | import org.bukkit.block.BlockFace;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | public final class FacingUtils {
14 |
15 | private FacingUtils() {}
16 |
17 | private final static List ROTATION_FACES = new ArrayList<>(Arrays.asList(
18 | BlockFace.NORTH_WEST,
19 | BlockFace.NORTH_NORTH_WEST,
20 | BlockFace.NORTH,
21 | BlockFace.NORTH_NORTH_EAST,
22 | BlockFace.NORTH_EAST,
23 |
24 | BlockFace.EAST_NORTH_EAST,
25 | BlockFace.EAST,
26 | BlockFace.EAST_SOUTH_EAST,
27 |
28 | BlockFace.SOUTH_EAST,
29 | BlockFace.SOUTH_SOUTH_EAST,
30 | BlockFace.SOUTH,
31 | BlockFace.SOUTH_SOUTH_WEST,
32 | BlockFace.SOUTH_WEST,
33 |
34 | BlockFace.WEST_SOUTH_WEST,
35 | BlockFace.WEST,
36 | BlockFace.WEST_NORTH_WEST
37 | ));
38 |
39 | /**
40 | * Returns the rotated version of a block face
41 | *
42 | * @param face face to be rotated
43 | * @param quarterTurns count of 90 degree turns that should be performed (0 - 3)
44 | */
45 | public static BlockFace getRotatedFace(BlockFace face, int quarterTurns) {
46 |
47 | if (!ROTATION_FACES.contains(face)) {
48 | return face;
49 | }
50 |
51 | int rotatedFaceIndex = (ROTATION_FACES.indexOf(face) + quarterTurns * 4) % ROTATION_FACES.size();
52 | return ROTATION_FACES.get(rotatedFaceIndex);
53 | }
54 |
55 | public static BlockFace[] getAxesFaces() {
56 | return new BlockFace[]{
57 | BlockFace.UP,
58 | BlockFace.DOWN,
59 | BlockFace.WEST,
60 | BlockFace.EAST,
61 | BlockFace.SOUTH,
62 | BlockFace.NORTH};
63 | }
64 |
65 | public static BlockVec[] getAxesBlockVecs() {
66 | return new BlockVec[]{
67 | new BlockVec(1, 0, 0),
68 | new BlockVec(0, 1, 0),
69 | new BlockVec(0, 0, 1),
70 | new BlockVec(-1, 0, 0),
71 | new BlockVec(0, -1, 0),
72 | new BlockVec(0, 0, -1)};
73 | }
74 |
75 | public static Axis getAxis(Block portalBlock) {
76 | return portalBlock.getData() == 2 ? Axis.Z : Axis.X;
77 | }
78 |
79 | public static EnumWrappers.Direction getBlockFaceToDirection(BlockFace face) {
80 | return EnumWrappers.Direction.valueOf(face.name());
81 | }
82 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/NmsUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.entity.Entity;
5 |
6 | import java.lang.reflect.InvocationTargetException;
7 | import java.lang.reflect.Method;
8 |
9 | public class NmsUtils {
10 |
11 | private static final String VERSION = Bukkit.getServer().getClass().getName().split("\\.")[3];
12 | private static Method ENTITY_GET_HANDLE;
13 | private static Method ENTITY_GET_DATA_WATCHER;
14 |
15 | static {
16 | try {
17 | ENTITY_GET_HANDLE = NmsUtils.getCraftBukkitClass("entity.CraftEntity").getMethod("getHandle");
18 | ENTITY_GET_DATA_WATCHER = NmsUtils.getNmsClass("Entity").getMethod("getDataWatcher");
19 | } catch (NoSuchMethodException | ClassNotFoundException e) {
20 | e.printStackTrace();
21 | }
22 | }
23 |
24 | public static Object getHandle(Entity entity) throws InvocationTargetException, IllegalAccessException {
25 | return ENTITY_GET_HANDLE.invoke(entity);
26 | }
27 |
28 | public static Object getDataWatcher(Entity entity) throws InvocationTargetException, IllegalAccessException {
29 | return ENTITY_GET_DATA_WATCHER.invoke(getHandle(entity));
30 | }
31 |
32 | public static Class> getNmsClass(String nmsClassString) throws ClassNotFoundException {
33 | return Class.forName("net.minecraft.server." + VERSION + "." + nmsClassString);
34 | }
35 |
36 | public static Class> getCraftBukkitClass(String cbClassString) throws ClassNotFoundException {
37 | return Class.forName("org.bukkit.craftbukkit." + VERSION + "." + cbClassString);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/TeleportUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import org.bukkit.entity.Player;
4 | import org.bukkit.plugin.java.JavaPlugin;
5 | import org.bukkit.scheduler.BukkitRunnable;
6 |
7 | import java.lang.reflect.Field;
8 | import java.lang.reflect.InvocationTargetException;
9 |
10 | public final class TeleportUtils {
11 |
12 | private TeleportUtils() {}
13 |
14 | private static Field FIELD_PLAYER_ABILITIES;
15 | private static Field FIELD_IS_INVULNERABLE;
16 |
17 | static {
18 |
19 | try {
20 | FIELD_PLAYER_ABILITIES = NmsUtils.getNmsClass("EntityPlayer").getField("abilities");
21 | FIELD_IS_INVULNERABLE = NmsUtils.getNmsClass("PlayerAbilities").getField("isInvulnerable");
22 |
23 | } catch (ClassNotFoundException | NoSuchFieldException e) {
24 | e.printStackTrace();
25 | }
26 | }
27 |
28 | public static void setTemporarilyInvulnerable(Player player, JavaPlugin plugin, long duration) {
29 |
30 | try {
31 | Object nmsPlayer = NmsUtils.getHandle(player);
32 | Object playerAbilities = FIELD_PLAYER_ABILITIES.get(nmsPlayer);
33 | FIELD_IS_INVULNERABLE.setBoolean(playerAbilities, true);
34 |
35 | new BukkitRunnable() {
36 | @Override
37 | public void run() {
38 |
39 | try {
40 | FIELD_IS_INVULNERABLE.setBoolean(playerAbilities, false);
41 | } catch (IllegalAccessException e) {
42 | e.printStackTrace();
43 | }
44 | }
45 | }.runTaskLater(plugin, duration);
46 |
47 | } catch (SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import java.time.LocalDate;
4 | import java.time.LocalTime;
5 | import java.time.Month;
6 | import java.time.temporal.ChronoUnit;
7 |
8 | public class TimeUtils {
9 |
10 | public static long getTicksTillNextMinute() {
11 |
12 | LocalTime now = LocalTime.now();
13 | LocalTime nextMinute = now.truncatedTo(ChronoUnit.MINUTES).plusMinutes(1);
14 | return now.until(nextMinute, ChronoUnit.MILLIS) / 50;
15 | }
16 |
17 | public static boolean isSpooktober() {
18 | return LocalDate.now().getMonth() == Month.OCTOBER;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/utils/VersionUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.utils;
2 |
3 | import org.bukkit.Bukkit;
4 |
5 | public final class VersionUtils {
6 |
7 | private VersionUtils() {}
8 |
9 | public static final String VERSION_STRING = Bukkit.getServer().getClass().getName().split("\\.")[3];
10 | private static final int[] CURRENT_VERSION_INTS = new int[3];
11 |
12 | static {
13 | String versionStringNumbersOnly = VERSION_STRING.replaceAll("[a-zA-Z]", "");
14 | System.arraycopy(getVersionAsIntArray(versionStringNumbersOnly, "_"), 0, CURRENT_VERSION_INTS, 0, 3);
15 | }
16 |
17 | public static final boolean IS_LEGACY_SERVER = !serverIsAtOrAbove("1.13.0");
18 |
19 | public static boolean isVersionLowerThan(String currentVersion, String requestedVersion) {
20 |
21 | int[] currentVersionInts = getVersionAsIntArray(currentVersion, "\\.");
22 | int[] requestedVersionInts = getVersionAsIntArray(requestedVersion, "\\.");
23 |
24 | for (int i = 0; i < Math.min(currentVersionInts.length, requestedVersionInts.length); i++) {
25 |
26 | int versionDiff = currentVersionInts[i] - requestedVersionInts[i];
27 |
28 | if (versionDiff > 0) {
29 | return false;
30 | }else if (versionDiff < 0) {
31 | return true;
32 | }
33 | }
34 |
35 | return requestedVersionInts.length > currentVersionInts.length;
36 | }
37 |
38 | public static boolean serverIsAtOrAbove(String requestedVersion) {
39 |
40 | int[] requestedVersionInts = getVersionAsIntArray(requestedVersion, "\\.");
41 |
42 | for (int i = 0; i < requestedVersionInts.length; i++) {
43 |
44 | int versionDiff = requestedVersionInts[i] - CURRENT_VERSION_INTS[i];
45 |
46 | if (versionDiff > 0) {
47 | return false;
48 | }else if (versionDiff < 0){
49 | return true;
50 | }
51 | }
52 |
53 | return true;
54 | }
55 |
56 | private static int[] getVersionAsIntArray(String version, String delimiter) {
57 |
58 | String[] split = version.split(delimiter);
59 |
60 | if (split.length > 3) {
61 | throw new IllegalArgumentException("Cannot process awfully long version string \"" + version + "\".");
62 | }
63 |
64 | int[] versionInts = new int[split.length];
65 |
66 | for (int i = 0; i < versionInts.length; i++) {
67 | versionInts[i] = Integer.parseInt(split[i]);
68 | }
69 |
70 | return versionInts;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/Axis.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper;
2 |
3 | import org.bukkit.util.Vector;
4 |
5 | /**
6 | * A replacement for the Axis enum of bukkit which did not exist before 1.13.
7 | * It is used to describe the orientation of portals which can be along the x-axis or the z-axis.
8 | */
9 | public enum Axis {
10 |
11 | X(new Vector(0, 0, 1), new Vector(1, 0, 0)),
12 | Z(new Vector(1, 0, 0), new Vector(0, 0, 1));
13 |
14 | private final Vector normal;
15 | private final Vector crossNormal;
16 |
17 | Axis(Vector normal, Vector crossNormal) {
18 | this.normal = normal;
19 | this.crossNormal = crossNormal;
20 | }
21 |
22 | /**
23 | * Returns the normal vector for the plane aligned to this axis.
24 | */
25 | public Vector getNormal() {
26 | return normal.clone();
27 | }
28 |
29 | /**
30 | * Returns a vector that is inside the plane aligned to this axis.
31 | * It like the cross product of the plane normal with the vector (0|1|0) but it's never negative.
32 | */
33 | public Vector getCrossNormal() {
34 | return crossNormal.clone();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/WrappedBoundingBox.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper;
2 |
3 | import me.gorgeousone.netherview.blockcache.BlockCache;
4 | import me.gorgeousone.netherview.geometry.viewfrustum.ViewFrustum;
5 | import me.gorgeousone.netherview.utils.NmsUtils;
6 | import me.gorgeousone.netherview.utils.VersionUtils;
7 | import org.bukkit.Location;
8 | import org.bukkit.entity.Entity;
9 | import org.bukkit.entity.EntityType;
10 | import org.bukkit.util.BoundingBox;
11 | import org.bukkit.util.Vector;
12 |
13 | import java.lang.reflect.Field;
14 | import java.lang.reflect.InvocationTargetException;
15 | import java.lang.reflect.Method;
16 | import java.util.ArrayList;
17 | import java.util.Arrays;
18 | import java.util.List;
19 |
20 | /**
21 | * A wrapper for the extent of a bounding boxes of an entity throughout different Minecraft versions.
22 | */
23 | public class WrappedBoundingBox {
24 |
25 | private static Method ENTITY_GET_AABB;
26 | private static Field AABB_MIN_X;
27 | private static Field AABB_MIN_Y;
28 | private static Field AABB_MIN_Z;
29 |
30 | private static Field AABB_MAX_X;
31 | private static Field AABB_MAX_Y;
32 | private static Field AABB_MAX_Z;
33 |
34 | static {
35 |
36 | if (VersionUtils.IS_LEGACY_SERVER) {
37 |
38 | try {
39 | ENTITY_GET_AABB = NmsUtils.getNmsClass("Entity").getDeclaredMethod("getBoundingBox");
40 | Class> aabbClass = NmsUtils.getNmsClass("AxisAlignedBB");
41 |
42 | AABB_MIN_X = aabbClass.getDeclaredField("a");
43 | AABB_MIN_Y = aabbClass.getDeclaredField("b");
44 | AABB_MIN_Z = aabbClass.getDeclaredField("c");
45 | AABB_MAX_X = aabbClass.getDeclaredField("d");
46 | AABB_MAX_Y = aabbClass.getDeclaredField("e");
47 | AABB_MAX_Z = aabbClass.getDeclaredField("f");
48 |
49 | } catch (NoSuchMethodException | ClassNotFoundException | NoSuchFieldException e) {
50 | e.printStackTrace();
51 | }
52 | }
53 | }
54 |
55 | private final double widthX;
56 | private final double widthZ;
57 | private final double height;
58 | private final List vertices;
59 |
60 | public WrappedBoundingBox(Entity entity, Location entityLoc, double widthX, double height, double widthZ) {
61 |
62 | this.widthX = widthX;
63 | this.widthZ = widthZ;
64 | this.height = height;
65 |
66 | //dunno, paintings are a bit off
67 | if (entity.getType() == EntityType.PAINTING && VersionUtils.IS_LEGACY_SERVER) {
68 | entityLoc.subtract(0, height / 2, 0);
69 | }
70 |
71 | Vector min = entityLoc.clone().subtract(
72 | widthX / 2,
73 | 0,
74 | widthZ / 2).toVector();
75 |
76 | Vector max = entityLoc.clone().add(
77 | widthX / 2,
78 | height,
79 | widthZ / 2).toVector();
80 |
81 | vertices = new ArrayList<>(Arrays.asList(
82 | min,
83 | new Vector(max.getX(), min.getY(), min.getZ()),
84 | new Vector(min.getX(), min.getY(), max.getZ()),
85 | new Vector(max.getX(), min.getY(), max.getZ()),
86 | new Vector(min.getX(), max.getY(), min.getZ()),
87 | new Vector(max.getX(), max.getY(), min.getZ()),
88 | new Vector(min.getX(), max.getY(), max.getZ()),
89 | max
90 | ));
91 | }
92 |
93 | public double getWidthX() {
94 | return widthX;
95 | }
96 |
97 | public double getWidthZ() {
98 | return widthZ;
99 | }
100 |
101 | public double getHeight() {
102 | return height;
103 | }
104 |
105 | /**
106 | * Returns the 8 vertices of the entity's bounding box
107 | */
108 | public List getVertices() {
109 | return vertices;
110 | }
111 |
112 | public static WrappedBoundingBox of(Entity entity) {
113 | return of(entity, entity.getLocation());
114 | }
115 |
116 | public static WrappedBoundingBox of(Entity entity, Location entityLoc) {
117 |
118 | if (VersionUtils.IS_LEGACY_SERVER) {
119 |
120 | try {
121 | Object entityAabb = ENTITY_GET_AABB.invoke(NmsUtils.getHandle(entity));
122 |
123 | return new WrappedBoundingBox(
124 | entity,
125 | entityLoc,
126 | getBoxWidthX(entityAabb),
127 | getBoxHeight(entityAabb),
128 | getBoxWidthZ(entityAabb));
129 |
130 | } catch (IllegalAccessException | InvocationTargetException e) {
131 |
132 | e.printStackTrace();
133 | return null;
134 | }
135 |
136 | } else {
137 |
138 | BoundingBox box = entity.getBoundingBox();
139 |
140 | return new WrappedBoundingBox(
141 | entity,
142 | entityLoc,
143 | box.getWidthX(),
144 | box.getHeight(),
145 | box.getWidthZ());
146 | }
147 | }
148 |
149 | private static double getBoxWidthX(Object aabb) throws IllegalAccessException {
150 | return AABB_MAX_X.getDouble(aabb) - AABB_MIN_X.getDouble(aabb);
151 | }
152 |
153 | private static double getBoxWidthZ(Object aabb) throws IllegalAccessException {
154 | return AABB_MAX_Z.getDouble(aabb) - AABB_MIN_Z.getDouble(aabb);
155 | }
156 |
157 | private static double getBoxHeight(Object aabb) throws IllegalAccessException {
158 | return AABB_MAX_Y.getDouble(aabb) - AABB_MIN_Y.getDouble(aabb);
159 | }
160 |
161 | /**
162 | * Returns true if any of the 8 vertices of the bounding box are inside of the block cache.
163 | */
164 | public boolean intersectsBlockCache(BlockCache cache) {
165 |
166 | for (Vector vertex : getVertices()) {
167 |
168 | if (cache.contains(vertex)) {
169 | return true;
170 | }
171 | }
172 | return false;
173 | }
174 |
175 | /**
176 | * Returns true if any of the 8 vertices of the bounding box are inside of the view frustum.
177 | */
178 | public boolean intersectsFrustum(ViewFrustum viewFrustum) {
179 |
180 | for (Vector vertex : getVertices()) {
181 |
182 | if (viewFrustum.contains(vertex)) {
183 | return true;
184 | }
185 | }
186 | return false;
187 | }
188 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/blocktype/AquaticBlockType.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper.blocktype;
2 |
3 | import com.comphenix.protocol.wrappers.WrappedBlockData;
4 | import me.gorgeousone.netherview.utils.FacingUtils;
5 | import me.gorgeousone.netherview.wrapper.rotation.AquaticRailUtils;
6 | import org.bukkit.Axis;
7 | import org.bukkit.Material;
8 | import org.bukkit.block.Block;
9 | import org.bukkit.block.BlockFace;
10 | import org.bukkit.block.BlockState;
11 | import org.bukkit.block.data.BlockData;
12 | import org.bukkit.block.data.Directional;
13 | import org.bukkit.block.data.MultipleFacing;
14 | import org.bukkit.block.data.Orientable;
15 | import org.bukkit.block.data.Rail;
16 | import org.bukkit.block.data.Rotatable;
17 | import org.bukkit.block.data.type.RedstoneWire;
18 |
19 | import java.util.HashMap;
20 | import java.util.HashSet;
21 | import java.util.Locale;
22 | import java.util.Map;
23 | import java.util.Objects;
24 | import java.util.Set;
25 |
26 | /**
27 | * Wrapper for the block data of blocks after the aquatic update (1.13)
28 | */
29 | public class AquaticBlockType extends BlockType {
30 |
31 | private final BlockData blockData;
32 |
33 | public AquaticBlockType(Material material) {
34 | blockData = material.createBlockData();
35 | }
36 |
37 | public AquaticBlockType(Block block) {
38 | blockData = block.getBlockData().clone();
39 | }
40 |
41 | public AquaticBlockType(BlockState state) {
42 | blockData = state.getBlockData().clone();
43 | }
44 |
45 | public AquaticBlockType(BlockData data) {
46 | blockData = data.clone();
47 | }
48 |
49 | public AquaticBlockType(String serialized) {
50 | blockData = Material.valueOf(serialized.toUpperCase(Locale.ENGLISH)).createBlockData();
51 | }
52 |
53 | @Override
54 | public BlockType rotate(int quarterTurns) {
55 |
56 | if (quarterTurns == 0) {
57 | return this;
58 | }
59 |
60 | //e.g. logs
61 | if (blockData instanceof Orientable) {
62 |
63 | if (quarterTurns % 2 == 0) {
64 | return this;
65 | }
66 |
67 | Orientable orientable = (Orientable) blockData;
68 |
69 | if (orientable.getAxis() != Axis.Y) {
70 | orientable.setAxis(orientable.getAxis() == Axis.X ? Axis.Z : Axis.X);
71 | }
72 |
73 | //e.g. furnaces, hoppers
74 | } else if (blockData instanceof Directional) {
75 |
76 | Directional directional = (Directional) blockData;
77 | directional.setFacing(FacingUtils.getRotatedFace(directional.getFacing(), quarterTurns));
78 |
79 | //e.g. signs
80 | } else if (blockData instanceof Rotatable) {
81 |
82 | Rotatable rotatable = (Rotatable) blockData;
83 | rotatable.setRotation(FacingUtils.getRotatedFace(rotatable.getRotation(), quarterTurns));
84 |
85 | //e.g. fences
86 | } else if (blockData instanceof MultipleFacing) {
87 |
88 | MultipleFacing multiFacing = (MultipleFacing) blockData;
89 | Set facings = new HashSet<>(multiFacing.getFaces());
90 |
91 | for (BlockFace face : multiFacing.getAllowedFaces()) {
92 | multiFacing.setFace(face, false);
93 | }
94 |
95 | for (BlockFace face : facings) {
96 | multiFacing.setFace(FacingUtils.getRotatedFace(face, quarterTurns), true);
97 | }
98 |
99 | } else if (blockData instanceof RedstoneWire) {
100 |
101 | RedstoneWire wire = (RedstoneWire) blockData;
102 | Map connections = new HashMap<>();
103 |
104 | for (BlockFace face : wire.getAllowedFaces()) {
105 | connections.put(face, wire.getFace(face));
106 | }
107 |
108 | for (BlockFace face : connections.keySet())
109 | wire.setFace(FacingUtils.getRotatedFace(face, quarterTurns), connections.get(face));
110 |
111 | } else if (blockData instanceof Rail) {
112 |
113 | Rail rail = (Rail) blockData;
114 | rail.setShape(AquaticRailUtils.getRotatedRail(rail.getShape(), quarterTurns));
115 | }
116 |
117 | return this;
118 | }
119 |
120 | @Override
121 | public WrappedBlockData getWrapped() {
122 | return WrappedBlockData.createData(blockData);
123 | }
124 |
125 | @Override
126 | public boolean isOccluding() {
127 | return blockData.getMaterial().isOccluding();
128 | }
129 |
130 | @Override
131 | public int hashCode() {
132 | return Objects.hash(blockData);
133 | }
134 |
135 | @Override
136 | public AquaticBlockType clone() {
137 | return new AquaticBlockType(blockData);
138 | }
139 |
140 | @Override
141 | public boolean equals(Object o) {
142 | if (this == o) {
143 | return true;
144 | }
145 | if (!(o instanceof AquaticBlockType)) {
146 | return false;
147 | }
148 | AquaticBlockType blockType = (AquaticBlockType) o;
149 | return blockData.equals(blockType.blockData);
150 | }
151 |
152 | @Override
153 | public String toString() {
154 | return "AquaBlock{" + blockData.getAsString() + '}';
155 | }
156 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/blocktype/BlockType.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper.blocktype;
2 |
3 | import com.comphenix.protocol.wrappers.WrappedBlockData;
4 | import org.bukkit.Material;
5 | import org.bukkit.block.Block;
6 | import org.bukkit.block.BlockState;
7 |
8 | /**
9 | * A wrapper for block materials and their (block-) data to support copying blocks across all Minecraft versions.
10 | */
11 | public abstract class BlockType {
12 |
13 | private static boolean isLegacyServer;
14 |
15 | /**
16 | * Sets whether BlockType.of() will create a LegacyBlockType or an AquaticBlockType
17 | */
18 | public static void configureVersion(boolean isLegacyServer) {
19 | BlockType.isLegacyServer = isLegacyServer;
20 | }
21 |
22 | public static BlockType of(Block block) {
23 | return isLegacyServer ? new LegacyBlockType(block) : new AquaticBlockType(block);
24 | }
25 |
26 | public static BlockType of(Material material) {
27 | return isLegacyServer ? new LegacyBlockType(material) : new AquaticBlockType(material);
28 | }
29 |
30 | public static BlockType of(BlockState state) {
31 | return isLegacyServer ? new LegacyBlockType(state) : new AquaticBlockType(state);
32 | }
33 |
34 | public static BlockType of(String serialized) {
35 | return isLegacyServer ? new LegacyBlockType(serialized) : new AquaticBlockType(serialized);
36 | }
37 |
38 | /**
39 | * Rotates the BlockType if it is rotatable in the xz plane in any way
40 | *
41 | * @param quarterTurns count of 90° turns performed (between 0 and 3)
42 | */
43 | public abstract BlockType rotate(int quarterTurns);
44 |
45 | public abstract WrappedBlockData getWrapped();
46 |
47 | public abstract boolean isOccluding();
48 |
49 | public abstract BlockType clone();
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/blocktype/LegacyBlockType.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper.blocktype;
2 |
3 | import com.comphenix.protocol.wrappers.WrappedBlockData;
4 | import me.gorgeousone.netherview.utils.FacingUtils;
5 | import org.bukkit.Material;
6 | import org.bukkit.block.Block;
7 | import org.bukkit.block.BlockFace;
8 | import org.bukkit.block.BlockState;
9 | import org.bukkit.material.Directional;
10 | import org.bukkit.material.MaterialData;
11 | import org.bukkit.material.Mushroom;
12 | import org.bukkit.material.Rails;
13 |
14 | import java.util.HashSet;
15 | import java.util.Locale;
16 | import java.util.Objects;
17 | import java.util.Set;
18 |
19 | /**
20 | * A wrapper for material data used before the aquatic update (1.12 and before)
21 | */
22 | @SuppressWarnings("deprecation")
23 | public class LegacyBlockType extends BlockType {
24 |
25 | private final MaterialData materialData;
26 |
27 | public LegacyBlockType(Material material) {
28 | materialData = new MaterialData(material);
29 | }
30 |
31 | public LegacyBlockType(Block block) {
32 | materialData = block.getState().getData().clone();
33 | }
34 |
35 | public LegacyBlockType(BlockState state) {
36 | materialData = state.getData().clone();
37 | }
38 |
39 | public LegacyBlockType(MaterialData data) {
40 | materialData = data.clone();
41 | }
42 |
43 | public LegacyBlockType(String serialized) {
44 |
45 | Material material;
46 | byte data = 0;
47 |
48 | if (serialized.contains(":")) {
49 |
50 | String[] fullData = serialized.split(":");
51 |
52 | material = Material.valueOf(fullData[0].toUpperCase(Locale.ENGLISH));
53 | data = Byte.parseByte(fullData[1]);
54 |
55 | } else {
56 | material = Material.valueOf(serialized.toUpperCase(Locale.ENGLISH));
57 | }
58 |
59 | materialData = new MaterialData(material, data);
60 | }
61 |
62 | @Override
63 | public BlockType rotate(int quarterTurns) {
64 |
65 | if (quarterTurns == 0) {
66 | return this;
67 | }
68 |
69 | if (materialData instanceof Directional) {
70 |
71 | Directional directional = (Directional) materialData;
72 | BlockFace facing = directional.getFacing();
73 |
74 | //somehow the facing of stairs is always reversed, nothing else, just stairs
75 | if (materialData.getItemType().name().contains("STAIRS")) {
76 | facing = facing.getOppositeFace();
77 | }
78 |
79 | directional.setFacingDirection(FacingUtils.getRotatedFace(facing, quarterTurns));
80 |
81 | } else if (materialData instanceof Rails) {
82 |
83 | Rails rails = (Rails) materialData;
84 | BlockFace facing = rails.getDirection();
85 | rails.setDirection(FacingUtils.getRotatedFace(facing, quarterTurns), rails.isOnSlope());
86 |
87 | } else if (materialData instanceof Mushroom) {
88 |
89 | Mushroom mushroom = (Mushroom) materialData;
90 | Set paintedFaces = new HashSet<>(mushroom.getPaintedFaces());
91 |
92 | for (BlockFace paintedFace : paintedFaces) {
93 | mushroom.setFacePainted(paintedFace, false);
94 | }
95 |
96 | for (BlockFace paintedFace : paintedFaces) {
97 | mushroom.setFacePainted(FacingUtils.getRotatedFace(paintedFace, quarterTurns), true);
98 | }
99 | }
100 | return this;
101 | }
102 |
103 | @Override
104 | public WrappedBlockData getWrapped() {
105 | return WrappedBlockData.createData(materialData.getItemType(), materialData.getData());
106 | }
107 |
108 | @Override
109 | public boolean isOccluding() {
110 | return materialData.getItemType().isOccluding();
111 | }
112 |
113 | @Override
114 | public LegacyBlockType clone() {
115 | return new LegacyBlockType(materialData.clone());
116 | }
117 |
118 | @Override
119 | public boolean equals(Object o) {
120 | if (this == o) {
121 | return true;
122 | }
123 | if (!(o instanceof LegacyBlockType)) {
124 | return false;
125 | }
126 | LegacyBlockType blockType = (LegacyBlockType) o;
127 | return materialData.equals(blockType.materialData);
128 | }
129 |
130 | @Override
131 | public int hashCode() {
132 | return Objects.hash(materialData);
133 | }
134 |
135 | @Override
136 | public String toString() {
137 | return "LegacyBlock{" + materialData.toString() + '}';
138 | }
139 | }
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/rotation/AquaticRailUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper.rotation;
2 |
3 | import org.bukkit.block.data.Rail;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | public class AquaticRailUtils {
10 |
11 | private final static List STRAIGHT_RAILS = new ArrayList<>(Arrays.asList(
12 | Rail.Shape.NORTH_SOUTH,
13 | Rail.Shape.EAST_WEST
14 | ));
15 |
16 | private final static List CURVED_RAILS = new ArrayList<>(Arrays.asList(
17 | Rail.Shape.NORTH_WEST,
18 | Rail.Shape.NORTH_EAST,
19 | Rail.Shape.SOUTH_EAST,
20 | Rail.Shape.SOUTH_WEST
21 | ));
22 |
23 | private final static List ASCENDING_RAILS = new ArrayList<>(Arrays.asList(
24 | Rail.Shape.ASCENDING_NORTH,
25 | Rail.Shape.ASCENDING_EAST,
26 | Rail.Shape.ASCENDING_SOUTH,
27 | Rail.Shape.ASCENDING_WEST
28 | ));
29 |
30 | public static Rail.Shape getRotatedRail(Rail.Shape railShape, int quarterTurns) {
31 |
32 | if (STRAIGHT_RAILS.contains(railShape)) {
33 |
34 | if (quarterTurns % 2 == 0) {
35 | return railShape;
36 | }
37 |
38 | return railShape == Rail.Shape.NORTH_SOUTH ? Rail.Shape.EAST_WEST : Rail.Shape.NORTH_SOUTH;
39 |
40 | } else if (CURVED_RAILS.contains(railShape)) {
41 |
42 | int rotatedShapeIndex = (CURVED_RAILS.indexOf(railShape) + quarterTurns) % CURVED_RAILS.size();
43 | return CURVED_RAILS.get(rotatedShapeIndex);
44 |
45 | } else {
46 |
47 | int rotatedShapeIndex = (ASCENDING_RAILS.indexOf(railShape) + quarterTurns) % ASCENDING_RAILS.size();
48 | return ASCENDING_RAILS.get(rotatedShapeIndex);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/me/gorgeousone/netherview/wrapper/rotation/RotationUtils.java:
--------------------------------------------------------------------------------
1 | package me.gorgeousone.netherview.wrapper.rotation;
2 |
3 | public class RotationUtils {
4 |
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/config.yml:
--------------------------------------------------------------------------------
1 | portal-display-range: 16
2 | portal-projection-distance: 4
3 | max-portal-size: 5
4 |
5 | hide-entities-behind-portals: true
6 | show-entities-inside-portals: true
7 | entity-update-ticks: 3
8 |
9 | warning-messages: true
10 | debug-messages: false
11 |
12 | nether-portal-viewing:
13 | flip-portals-by-default: true
14 | hide-portal-blocks: true
15 | cancel-teleport-when-linking-portals: true
16 | instant-teleport: true
17 | whitelisted-worlds:
18 | - world
19 | - world_nether
20 | blacklisted-worlds:
21 | - null
22 |
23 | custom-portal-viewing:
24 | whitelisted-worlds:
25 | - "*"
26 | blacklisted-worlds:
27 | - null
--------------------------------------------------------------------------------
/src/main/resources/config2.yml:
--------------------------------------------------------------------------------
1 | max-portal-size: 5
2 | portal-view-distance: 4
3 | portal-display-range: 32
4 | flip-portals-by-default: false
5 | hide-portal-blocks: true
6 | cancel-teleport-when-linking-portals: true
7 | instant-teleport: true
8 | hide-entities-behind-portals: true
9 | show-entities-inside-portals: true
10 | warning-messages: true
11 | debug-messages: false
12 | worlds-with-portal-viewing:
13 | - world
14 | - world_nether
15 | world-black-list:
16 | - null
17 |
--------------------------------------------------------------------------------
/src/main/resources/language.yml:
--------------------------------------------------------------------------------
1 | successful-portal-linking: '&7&oThe veil between the two worlds has lifted a little bit!'
2 | unequal-portal-sizes: '&7These two portals are not the same size.'
3 | portal-frame-incomplete: '&7All frame blocks of this %world-type%-world portal need to be occluding blocks.'
4 | portal-corners-incomplete: '&7All four corners corners of this %world-type%-world portal need to be occluding blocks.'
5 | portal-too-big: '&7Cannot make portal views bigger than %size% blocks!'
6 | portal-not-intact: '&7Some portal blocks of this %world-type%-world portal seem to be broken.'
7 | world-not-white-listed: |-
8 | &7NetherView is not enabled in world &r%world%&7.
9 | &7You can enable it by adding the world's name to 'worlds-with-portal-viewing' in the config.
10 | no-world-found: '&7No world found with name &r%world%&7.'
11 | no-portals-found: '&7There are no nether portals listed for world &r%world%&7.'
12 | no-portal-found-nearby: '&7You need to look at a nether portal with NetherView enabled for this command to work.'
13 | portal-info: '&7Info about portal at &r%location%: %info%'
14 |
15 | world-info: '%count% &7portal(s) listed for world &r%world%&7: %portals%'
16 | flipped-portal: '&7Flipped view of portal &r%portal%&7.'
17 | portal-viewing-on: '&7Enabled portal viewing for you.'
18 | portal-viewing-off: '&7Disabled portal viewing for you.'
19 |
20 | wand-info: '&7Left click to set first portal corner. Right click to set the second one. Use &r/nv createportal&7 to create the portal.'
21 | selection-incomplete: '&7Please select two blocks with a portal wand first.'
22 | set-first-position: '&7First position set %position%.'
23 | set-second-position: '&7Second position set %position%.'
24 | selection-too-small: '&7Your selection is too small to create a portal.'
25 | selection-not-flat: '&7A selection must be 1 block wide in either X or Z direction.'
26 | portals-intersect: '&7This portal would overlap with another portal that is already here.'
27 | portal-name-not-valid: '&7A portal name can be max. 32 characters long and may only contain alphanumeric letters and dashes/underscores.'
28 | portal-name-not-unique: '&7There is already a portal named &r%name%&7.'
29 | created-portal: '&7Created portal &r%name%&7 (%size% blocks).'
30 | no-portal-found-with-name: '&7No portal found with name &r%name%&7.'
31 | deleted-portal: '&7Removed portal &r%name%&7.'
32 | linked-portal: '&7Linked portal &r%from-portal%&7 to portal &r%to-portal%&7.'
33 | unlinked-portal: '&7Removed link of portal &r%portal%&7.'
34 |
--------------------------------------------------------------------------------
/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: NetherView
2 | version: ${project.version}
3 | api-version: 1.13
4 |
5 | main: me.gorgeousone.netherview.NetherViewPlugin
6 |
7 | softdepend: [ProtocolLib]
8 |
9 | commands:
10 | netherview:
11 | aliases: [nv]
12 | toggleportalview:
13 | description: Toggles whether you can see potal projections or not for yourself.
14 |
15 | permissions:
16 | netherview.viewportals:
17 | default: true
18 | netherview.linkportals:
19 | default: true
20 | netherview.config:
21 | default: op
22 | netherview.info:
23 | default: op
24 | netherview.flipportal:
25 | default: op
26 | netherview.portalwand:
27 | default: op
28 | netherview.customportals:
29 | defult: op
--------------------------------------------------------------------------------