diff --git a/pom.xml b/pom.xml
index 1de5ef5..835a721 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.TheDgtl
Stargate
- 0.7.9.11-SNAPSHOT
+ 0.8.0.0-SNAPSHOT
UTF-8
@@ -20,7 +20,7 @@
org.spigotmc
spigot-api
- 1.12.2-R0.1-SNAPSHOT
+ 1.13.1-R0.1-SNAPSHOT
net.milkbowl.vault
@@ -51,8 +51,8 @@
maven-compiler-plugin
3.6.1
-
- 1.7
+
+ 1.8
diff --git a/src/config.yml b/src/config.yml
index 71d8ef1..8bffb5e 100644
--- a/src/config.yml
+++ b/src/config.yml
@@ -12,7 +12,8 @@
# handleVehicles - Whether to allow vehicles through gates
# sortLists - Whether to sort network lists alphabetically
# protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material)
-# signColor - The color used for drawing signs (Default: BLACK). See:
+# signColor - The color used for drawing signs (Default: BLACK).
+# verifyPortals - Whether or not all the non-sign blocks are checked to match the gate layout when a stargate is loaded.
############################
# Stargate economy options #
############################
@@ -49,4 +50,5 @@ chargefreedestination: true
freegatesgreen: false
debug: false
permdebug: false
-enableBungee: false
\ No newline at end of file
+enableBungee: false
+verifyPortals: true
\ No newline at end of file
diff --git a/src/net/TheDgtl/Stargate/Blox.java b/src/net/TheDgtl/Stargate/Blox.java
index 31f0da7..0fb1608 100644
--- a/src/net/TheDgtl/Stargate/Blox.java
+++ b/src/net/TheDgtl/Stargate/Blox.java
@@ -4,6 +4,9 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.type.Sign;
+import org.bukkit.block.data.type.WallSign;
/**
* Stargate - A portal plugin for Bukkit
@@ -85,14 +88,6 @@ public class Blox {
return world.getBlockAt(x, y, z).getType();
}
- public void setData(int data) {
- world.getBlockAt(x, y, z).setData((byte)data);
- }
-
- public int getData() {
- return world.getBlockAt(x, y, z).getData();
- }
-
public Block getBlock() {
return world.getBlockAt(x, y, z);
}
@@ -125,16 +120,10 @@ public class Blox {
int offsetZ = 0;
if (getBlock().getType() == Material.WALL_SIGN) {
- if (getData() == 0x2) {
- offsetZ = 1;
- } else if (getData() == 0x3) {
- offsetZ = -1;
- } else if (getData() == 0x4) {
- offsetX = 1;
- } else if (getData() == 0x5) {
- offsetX = -1;
- }
- } else if (getBlock().getType() == Material.SIGN_POST) {
+ BlockFace facing = ((WallSign) getBlock().getBlockData()).getFacing().getOppositeFace();
+ offsetX = facing.getModX();
+ offsetZ = facing.getModZ();
+ } else if (getBlock().getType() == Material.SIGN) {
offsetY = -1;
} else {
return;
diff --git a/src/net/TheDgtl/Stargate/BloxPopulator.java b/src/net/TheDgtl/Stargate/BloxPopulator.java
index fd18537..88290fb 100644
--- a/src/net/TheDgtl/Stargate/BloxPopulator.java
+++ b/src/net/TheDgtl/Stargate/BloxPopulator.java
@@ -1,22 +1,23 @@
package net.TheDgtl.Stargate;
+import org.bukkit.Axis;
import org.bukkit.Material;
public class BloxPopulator {
private Blox blox;
private Material nextMat;
- private byte nextData;
+ private Axis nextAxis;
public BloxPopulator(Blox b, Material m) {
blox = b;
nextMat = m;
- nextData = 0;
+ nextAxis = null;
}
- public BloxPopulator(Blox b, Material m, byte d) {
+ public BloxPopulator(Blox b, Material m, Axis a) {
blox = b;
nextMat = m;
- nextData = d;
+ nextAxis = a;
}
public void setBlox(Blox b) {
@@ -27,8 +28,8 @@ public class BloxPopulator {
nextMat = m;
}
- public void setData(byte d) {
- nextData = d;
+ public void setAxis(Axis a) {
+ nextAxis = a;
}
public Blox getBlox() {
@@ -39,8 +40,8 @@ public class BloxPopulator {
return nextMat;
}
- public byte getData() {
- return nextData;
+ public Axis getAxis() {
+ return nextAxis;
}
}
diff --git a/src/net/TheDgtl/Stargate/Gate.java b/src/net/TheDgtl/Stargate/Gate.java
index 7177166..99609aa 100644
--- a/src/net/TheDgtl/Stargate/Gate.java
+++ b/src/net/TheDgtl/Stargate/Gate.java
@@ -51,7 +51,7 @@ public class Gate {
private RelativeBlockVector[] controls = new RelativeBlockVector[0];
private RelativeBlockVector exitBlock = null;
private HashMap exits = new HashMap<>();
- private Material portalBlockOpen = Material.PORTAL;
+ private Material portalBlockOpen = Material.NETHER_PORTAL;
private Material portalBlockClosed = Material.AIR;
// Economy information
@@ -138,7 +138,9 @@ public class Gate {
bw.append(type);
bw.append('=');
- bw.append(value.toString());
+ if(value != null) {
+ bw.append(value.toString());
+ }
bw.newLine();
}
@@ -247,6 +249,7 @@ public class Gate {
}
public boolean matches(Blox topleft, int modX, int modZ, boolean onCreate) {
+ HashMap portalTypes = new HashMap<>(types);
for (int y = 0; y < layout.length; y++) {
for (int x = 0; x < layout[y].length; x++) {
Character key = layout[y][x];
@@ -260,27 +263,17 @@ public class Gate {
if (onCreate && type == Material.AIR) continue;
if (type != portalBlockClosed && type != portalBlockOpen) {
- // Special case for water gates
- if (portalBlockOpen == Material.WATER || portalBlockOpen == Material.STATIONARY_WATER) {
- if (type == Material.WATER || type == Material.STATIONARY_WATER) {
- continue;
- }
- }
- // Special case for lava gates
- if (portalBlockOpen == Material.LAVA || portalBlockOpen == Material.STATIONARY_LAVA) {
- if (type == Material.LAVA || type == Material.STATIONARY_LAVA) {
- continue;
- }
- }
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
return false;
}
} else if (!key.equals(ANYTHING)) {
- Material id = types.get(key);
- if (topleft.modRelative(x, y, 0, modX, 1, modZ).getType() != id) {
- Stargate.debug("Gate::Matches", "Block Type Mismatch: " + topleft.modRelative(x, y, 0, modX, 1, modZ).getType() + " != " + id);
- return false;
- }
+ Material id = portalTypes.get(key);
+ if(id == null) {
+ portalTypes.put(key, topleft.modRelative(x, y, 0, modX, 1, modZ).getType());
+ } else if(topleft.modRelative(x, y, 0, modX, 1, modZ).getType() != id) {
+ Stargate.debug("Gate::Matches", "Block Type Mismatch: " + topleft.modRelative(x, y, 0, modX, 1, modZ).getType() + " != " + id);
+ return false;
+ }
}
}
}
@@ -294,7 +287,7 @@ public class Gate {
Material blockID = gate.getControlBlock();
if (!controlBlocks.containsKey(blockID)) {
- controlBlocks.put(blockID, new ArrayList());
+ controlBlocks.put(blockID, new ArrayList<>());
}
controlBlocks.get(blockID).add(gate);
diff --git a/src/net/TheDgtl/Stargate/Portal.java b/src/net/TheDgtl/Stargate/Portal.java
index 86faeaf..53fa120 100644
--- a/src/net/TheDgtl/Stargate/Portal.java
+++ b/src/net/TheDgtl/Stargate/Portal.java
@@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.util.logging.Level;
@@ -18,20 +19,23 @@ import net.TheDgtl.Stargate.event.StargateDeactivateEvent;
import net.TheDgtl.Stargate.event.StargateOpenEvent;
import net.TheDgtl.Stargate.event.StargatePortalEvent;
+import org.bukkit.Axis;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.Directional;
+import org.bukkit.block.data.Powerable;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.minecart.StorageMinecart;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.player.PlayerMoveEvent;
-import org.bukkit.material.Button;
-import org.bukkit.material.MaterialData;
import org.bukkit.material.Step;
import org.bukkit.util.Vector;
@@ -72,6 +76,7 @@ public class Portal {
private int modX;
private int modZ;
private float rotX;
+ private Axis rot;
// Block references
private Blox id;
@@ -117,6 +122,7 @@ public class Portal {
this.modX = modX;
this.modZ = modZ;
this.rotX = rotX;
+ this.rot = rotX == 90.0F || rotX == 270.0F ? Axis.X : Axis.Z;
this.id = id;
this.destination = dest;
this.button = button;
@@ -234,6 +240,10 @@ public class Portal {
public float getRotation() {
return rotX;
}
+
+ public Axis getAxis() {
+ return rot;
+ }
public Player getActivePlayer() {
return activePlayer;
@@ -366,8 +376,9 @@ public class Portal {
getWorld().loadChunk(getWorld().getChunkAt(topLeft.getBlock()));
Material openType = gate.getPortalBlockOpen();
+ Axis ax = openType == Material.NETHER_PORTAL ? rot : null;
for (Blox inside : getEntrances()) {
- Stargate.blockPopulatorQueue.add(new BloxPopulator(inside, openType));
+ Stargate.blockPopulatorQueue.add(new BloxPopulator(inside, openType, ax));
}
isOpen = true;
@@ -442,10 +453,11 @@ public class Portal {
RelativeBlockVector[] controls = gate.getControls();
for (RelativeBlockVector vector : controls) {
- MaterialData mat = getBlockAt(vector).getBlock().getState().getData();
+ BlockData data = getBlockAt(vector).getBlock().getBlockData();
- if (mat instanceof Button && ((Button)mat).isPowered())
+ if (data instanceof Powerable && ((Powerable) data).isPowered()) {
return true;
+ }
}
return false;
@@ -496,34 +508,20 @@ public class Portal {
vehicle.setVelocity(new Vector());
// Get new velocity
- final Vector newVelocity = new Vector();
- switch (id.getBlock().getData()) {
- case 2:
- newVelocity.setZ(-1);
- break;
- case 3:
- newVelocity.setZ(1);
- break;
- case 4:
- newVelocity.setX(-1);
- break;
- case 5:
- newVelocity.setX(1);
- break;
- }
+ final Vector newVelocity = new Vector(modX, 0.0F, modZ);
newVelocity.multiply(velocity);
- final Entity passenger = vehicle.getPassenger();
- if (passenger != null) {
+ List passengers = vehicle.getPassengers();
+ if (!passengers.isEmpty()) {
final Vehicle v = exit.getWorld().spawn(exit, vehicle.getClass());
+ final Entity passenger = passengers.get(0);
vehicle.eject();
vehicle.remove();
+ passenger.eject();
passenger.teleport(exit);
- Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, new Runnable() {
- public void run() {
- v.setPassenger(passenger);
- v.setVelocity(newVelocity);
- }
+ Stargate.server.getScheduler().scheduleSyncDelayedTask(Stargate.stargate, () -> {
+ v.addPassenger(passenger);
+ v.setVelocity(newVelocity);
}, 1);
} else {
Vehicle mc = exit.getWorld().spawn(exit, vehicle.getClass());
@@ -568,6 +566,9 @@ public class Portal {
public boolean isVerified() {
verified = true;
+ if(!Stargate.verifyPortals) {
+ return true;
+ }
for (RelativeBlockVector control : gate.getControls()) {
verified = verified && getBlockAt(control).getBlock().getType().equals(gate.getControlBlock());
}
@@ -575,10 +576,16 @@ public class Portal {
}
public boolean wasVerified() {
+ if(!Stargate.verifyPortals) {
+ return true;
+ }
return verified;
}
public boolean checkIntegrity() {
+ if(!Stargate.verifyPortals) {
+ return true;
+ }
return gate.matches(topLeft, modX, modZ);
}
@@ -692,7 +699,7 @@ public class Portal {
public final void drawSign() {
Material sMat = id.getBlock().getType();
- if (sMat != Material.SIGN && sMat != Material.WALL_SIGN && sMat != Material.SIGN_POST) {
+ if (sMat != Material.SIGN && sMat != Material.WALL_SIGN) {
Stargate.log.warning("[Stargate] Sign block is not a Sign object");
Stargate.debug("Portal::drawSign", "Block: " + id.getBlock().getType() + " @ " + id.getBlock().getLocation());
return;
@@ -854,14 +861,14 @@ public class Portal {
// Check if network exists in our network list
if (!lookupNamesNet.containsKey(getNetwork().toLowerCase())) {
Stargate.debug("register", "Network " + getNetwork() + " not in lookupNamesNet, adding");
- lookupNamesNet.put(getNetwork().toLowerCase(), new HashMap());
+ lookupNamesNet.put(getNetwork().toLowerCase(), new HashMap<>());
}
lookupNamesNet.get(getNetwork().toLowerCase()).put(getName().toLowerCase(), this);
// Check if this network exists
if (!allPortalsNet.containsKey(getNetwork().toLowerCase())) {
Stargate.debug("register", "Network " + getNetwork() + " not in allPortalsNet, adding");
- allPortalsNet.put(getNetwork().toLowerCase(), new ArrayList());
+ allPortalsNet.put(getNetwork().toLowerCase(), new ArrayList<>());
}
allPortalsNet.get(getNetwork().toLowerCase()).add(getName().toLowerCase());
}
@@ -952,24 +959,24 @@ public class Portal {
int modX = 0;
int modZ = 0;
float rotX = 0f;
- int facing = 0;
+ BlockFace buttonfacing = BlockFace.DOWN;
if (idParent.getX() > id.getBlock().getX()) {
modZ -= 1;
rotX = 90f;
- facing = 2;
+ buttonfacing = BlockFace.WEST;
} else if (idParent.getX() < id.getBlock().getX()) {
modZ += 1;
rotX = 270f;
- facing = 1;
+ buttonfacing = BlockFace.EAST;
} else if (idParent.getZ() > id.getBlock().getZ()) {
modX += 1;
rotX = 180f;
- facing = 4;
+ buttonfacing = BlockFace.NORTH;
} else if (idParent.getZ() < id.getBlock().getZ()) {
modX -= 1;
rotX = 0f;
- facing = 3;
+ buttonfacing = BlockFace.SOUTH;
}
Gate[] possibleGates = Gate.getGatesByControlBlock(idParent);
@@ -1145,7 +1152,9 @@ public class Portal {
if (!alwaysOn) {
button = topleft.modRelative(buttonVector.getRight(), buttonVector.getDepth(), buttonVector.getDistance() + 1, modX, 1, modZ);
button.setType(Material.STONE_BUTTON);
- button.setData(facing);
+ Directional buttondata = (Directional) button.getBlock().getBlockData();
+ buttondata.setFacing(buttonfacing);
+ button.getBlock().setBlockData(buttondata);
portal.setButton(button);
}
@@ -1198,6 +1207,34 @@ public class Portal {
public static Portal getByEntrance(Block block) {
return lookupEntrances.get(new Blox(block));
}
+
+ public static Portal getByAdjacentEntrance(Location loc) {
+ int centerX = loc.getBlockX();
+ int centerY = loc.getBlockY();
+ int centerZ = loc.getBlockZ();
+ World world = loc.getWorld();
+ Portal portal = lookupEntrances.get(new Blox(world, centerX, centerY, centerZ));
+ if(portal != null) {
+ return portal;
+ }
+ portal = lookupEntrances.get(new Blox(world, centerX + 1, centerY, centerZ));
+ if(portal != null) {
+ return portal;
+ }
+ portal = lookupEntrances.get(new Blox(world, centerX - 1, centerY, centerZ));
+ if(portal != null) {
+ return portal;
+ }
+ portal = lookupEntrances.get(new Blox(world, centerX, centerY, centerZ + 1));
+ if(portal != null) {
+ return portal;
+ }
+ portal = lookupEntrances.get(new Blox(world, centerX, centerY, centerZ - 1));
+ if(portal != null) {
+ return portal;
+ }
+ return null;
+ }
public static Portal getByControl(Block block) {
return lookupControls.get(new Blox(block));
@@ -1355,18 +1392,17 @@ public class Portal {
// DEBUG
for (RelativeBlockVector control : portal.getGate().getControls()) {
if (!portal.getBlockAt(control).getBlock().getType().equals(portal.getGate().getControlBlock())) {
- Stargate.debug("loadAllGates", "Control Block Type == " + portal.getBlockAt(control).getBlock().getTypeId());
+ Stargate.debug("loadAllGates", "Control Block Type == " + portal.getBlockAt(control).getBlock().getType().name());
}
}
portal.unregister(false);
iter.remove();
Stargate.log.info("[Stargate] Destroying stargate at " + portal.toString());
continue;
- } else {
- portal.drawSign();
- portalCount++;
}
}
+ portal.drawSign();
+ portalCount++;
if (!portal.isFixed()) continue;
diff --git a/src/net/TheDgtl/Stargate/Stargate.java b/src/net/TheDgtl/Stargate/Stargate.java
index 546b676..7272a48 100644
--- a/src/net/TheDgtl/Stargate/Stargate.java
+++ b/src/net/TheDgtl/Stargate/Stargate.java
@@ -19,12 +19,13 @@ import net.TheDgtl.Stargate.event.StargateDestroyEvent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
-import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.block.Block;
+import org.bukkit.block.EndGateway;
import org.bukkit.block.Sign;
+import org.bukkit.block.data.Orientable;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
@@ -46,7 +47,7 @@ import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
-import org.bukkit.event.player.PlayerPortalEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
@@ -100,6 +101,7 @@ public class Stargate extends JavaPlugin {
public static boolean sortLists = false;
public static boolean protectEntrance = false;
public static boolean enableBungee = true;
+ public static boolean verifyPortals = true;
public static ChatColor signColor;
// Temp workaround for snowmen, don't check gate entrance
@@ -191,6 +193,7 @@ public class Stargate extends JavaPlugin {
sortLists = newConfig.getBoolean("sortLists");
protectEntrance = newConfig.getBoolean("protectEntrance");
enableBungee = newConfig.getBoolean("enableBungee");
+ verifyPortals = newConfig.getBoolean("verifyPortals");
// Sign color
String sc = newConfig.getString("signColor");
try {
@@ -681,32 +684,14 @@ public class Stargate extends JavaPlugin {
}
@EventHandler
- public void onPlayerPortal(PlayerPortalEvent event) {
- if (event.isCancelled()) return;
- // Do a quick check for a stargate
- Location from = event.getFrom();
- if (from == null) {
- Stargate.debug("onPlayerPortal", "From location is null. Stupid Bukkit");
- return;
- }
- World world = from.getWorld();
- int cX = from.getBlockX();
- int cY = from.getBlockY();
- int cZ = from.getBlockZ();
- for (int i = -2; i < 2; i++) {
- for (int j = -2; j < 2; j++) {
- for (int k = -2; k < 2; k++) {
- Block b = world.getBlockAt(cX + i, cY + j, cZ + k);
- // We only need to worry about portal mat
- // Commented out for now, due to new Minecraft insta-nether
- //if (b.getType() != Material.PORTAL) continue;
- Portal portal = Portal.getByEntrance(b);
- if (portal != null) {
- event.setCancelled(true);
- return;
- }
- }
- }
+ public void onPlayerTeleport(PlayerTeleportEvent event) {
+ // cancel portal and endgateway teleportation if it's from a Stargate entrance
+ PlayerTeleportEvent.TeleportCause cause = event.getCause();
+ if(!event.isCancelled()
+ && (cause == PlayerTeleportEvent.TeleportCause.NETHER_PORTAL
+ || cause == PlayerTeleportEvent.TeleportCause.END_GATEWAY && World.Environment.THE_END == event.getFrom().getWorld().getEnvironment())
+ && Portal.getByAdjacentEntrance(event.getFrom()) != null) {
+ event.setCancelled(true);
}
}
@@ -1012,7 +997,7 @@ public class Stargate extends JavaPlugin {
Portal portal = null;
// Handle keeping portal material and buttons around
- if (block.getType() == Material.PORTAL) {
+ if (block.getType() == Material.NETHER_PORTAL) {
portal = Portal.getByEntrance(block);
} else if (block.getType() == Material.STONE_BUTTON) {
portal = Portal.getByControl(block);
@@ -1056,11 +1041,7 @@ public class Stargate extends JavaPlugin {
private class wListener implements Listener {
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
- World w = event.getWorld();
- // We have to make sure the world is actually loaded. This gets called twice for some reason.
- if (w.getBlockAt(w.getSpawnLocation()).getWorld() != null) {
- Portal.loadAllGates(w);
- }
+ Portal.loadAllGates(event.getWorld());
}
// We need to reload all gates on world unload, boo
@@ -1090,8 +1071,8 @@ public class Stargate extends JavaPlugin {
if (destroyExplosion) {
portal.unregister(true);
} else {
- Stargate.blockPopulatorQueue.add(new BloxPopulator(new Blox(b), b.getType(), b.getData()));
event.setCancelled(true);
+ break;
}
}
}
@@ -1116,11 +1097,22 @@ public class Stargate extends JavaPlugin {
private class BlockPopulatorThread implements Runnable {
public void run() {
long sTime = System.nanoTime();
- while (System.nanoTime() - sTime < 50000000) {
+ while (System.nanoTime() - sTime < 25000000) {
BloxPopulator b = Stargate.blockPopulatorQueue.poll();
if (b == null) return;
- b.getBlox().getBlock().setType(b.getMat(), false);
- b.getBlox().getBlock().setData(b.getData(), false);
+ Block blk = b.getBlox().getBlock();
+ blk.setType(b.getMat(), false);
+ if(b.getMat() == Material.END_GATEWAY && blk.getWorld().getEnvironment() == World.Environment.THE_END) {
+ // force a location to prevent exit gateway generation
+ EndGateway gateway = (EndGateway) blk.getState();
+ gateway.setExitLocation(blk.getWorld().getSpawnLocation());
+ gateway.setExactTeleport(true);
+ gateway.update(false, false);
+ } else if(b.getAxis() != null) {
+ Orientable orientable = (Orientable) blk.getBlockData();
+ orientable.setAxis(b.getAxis());
+ blk.setBlockData(orientable);
+ }
}
}
}
diff --git a/src/plugin.yml b/src/plugin.yml
index 1b79cc2..3fc13e4 100644
--- a/src/plugin.yml
+++ b/src/plugin.yml
@@ -4,6 +4,7 @@ version: 0.7.9.11
description: Stargate mod for Bukkit
author: Drakia
website: http://www.thedgtl.net
+api-version: 1.13
commands:
sg:
description: Used to reload the plugin. Console use only.