mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-12 10:38:34 +00:00
Switch to Gradle. Use git log --follow for history.
This converts the project into a multi-module Gradle build. By default, Git does not show history past a rename, so use git log --follow to see further history.
This commit is contained in:
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.math.transform.Identity;
|
||||
import com.sk89q.worldedit.math.transform.Transform;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Holds the clipboard and the current transform on the clipboard.
|
||||
*/
|
||||
public class ClipboardHolder {
|
||||
|
||||
private final WorldData worldData;
|
||||
private final Clipboard clipboard;
|
||||
private Transform transform = new Identity();
|
||||
|
||||
/**
|
||||
* Create a new instance with the given clipboard.
|
||||
*
|
||||
* @param clipboard the clipboard
|
||||
* @param worldData the mapping of blocks, entities, and so on
|
||||
*/
|
||||
public ClipboardHolder(Clipboard clipboard, WorldData worldData) {
|
||||
checkNotNull(clipboard);
|
||||
checkNotNull(worldData);
|
||||
this.clipboard = clipboard;
|
||||
this.worldData = worldData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping used for blocks, entities, and so on.
|
||||
*
|
||||
* @return the mapping
|
||||
*/
|
||||
public WorldData getWorldData() {
|
||||
return worldData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clipboard.
|
||||
* <p>
|
||||
* If there is a transformation applied, the returned clipboard will
|
||||
* not contain its effect.
|
||||
*
|
||||
* @return the clipboard
|
||||
*/
|
||||
public Clipboard getClipboard() {
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transform.
|
||||
*
|
||||
* @param transform the transform
|
||||
*/
|
||||
public void setTransform(Transform transform) {
|
||||
checkNotNull(transform);
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transform.
|
||||
*
|
||||
* @return the transform
|
||||
*/
|
||||
public Transform getTransform() {
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for an operation to paste this clipboard.
|
||||
*
|
||||
* @return a builder
|
||||
*/
|
||||
public PasteBuilder createPaste(Extent targetExtent, WorldData targetWorldData) {
|
||||
return new PasteBuilder(this, targetExtent, targetWorldData);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
/**
|
||||
* Raised when the session is missing.
|
||||
*/
|
||||
public class MissingSessionException extends Exception {
|
||||
|
||||
public MissingSessionException() {
|
||||
}
|
||||
|
||||
public MissingSessionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MissingSessionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public MissingSessionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
|
||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
|
||||
import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.math.transform.Transform;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Builds an operation to paste the contents of a clipboard.
|
||||
*/
|
||||
public class PasteBuilder {
|
||||
|
||||
private final Clipboard clipboard;
|
||||
private final WorldData worldData;
|
||||
private final Transform transform;
|
||||
private final Extent targetExtent;
|
||||
private final WorldData targetWorldData;
|
||||
|
||||
private Vector to = new Vector();
|
||||
private boolean ignoreAirBlocks;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param holder the clipboard holder
|
||||
* @param targetExtent an extent
|
||||
* @param targetWorldData world data of the target
|
||||
*/
|
||||
PasteBuilder(ClipboardHolder holder, Extent targetExtent, WorldData targetWorldData) {
|
||||
checkNotNull(holder);
|
||||
checkNotNull(targetExtent);
|
||||
checkNotNull(targetWorldData);
|
||||
this.clipboard = holder.getClipboard();
|
||||
this.worldData = holder.getWorldData();
|
||||
this.transform = holder.getTransform();
|
||||
this.targetExtent = targetExtent;
|
||||
this.targetWorldData = targetWorldData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target location.
|
||||
*
|
||||
* @param to the target location
|
||||
* @return this builder instance
|
||||
*/
|
||||
public PasteBuilder to(Vector to) {
|
||||
this.to = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether air blocks in the source are skipped over when pasting.
|
||||
*
|
||||
* @return this builder instance
|
||||
*/
|
||||
public PasteBuilder ignoreAirBlocks(boolean ignoreAirBlocks) {
|
||||
this.ignoreAirBlocks = ignoreAirBlocks;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the operation.
|
||||
*
|
||||
* @return the operation
|
||||
*/
|
||||
public Operation build() {
|
||||
BlockTransformExtent extent = new BlockTransformExtent(clipboard, transform, targetWorldData.getBlockRegistry());
|
||||
ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), targetExtent, to);
|
||||
copy.setTransform(transform);
|
||||
if (ignoreAirBlocks) {
|
||||
copy.setSourceMask(new ExistingBlockMask(clipboard));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
import com.sk89q.worldedit.util.Identifiable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides information about a session.
|
||||
*
|
||||
* <p>A reference for this object may be kept around for a long time.</p>
|
||||
*/
|
||||
public interface SessionKey extends Identifiable {
|
||||
|
||||
/**
|
||||
* Get the name for this session, if one is available, so that it can
|
||||
* be referred to by others.
|
||||
*
|
||||
* @return a name or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return whether the session is still active. Sessions that are inactive
|
||||
* for a prolonged amount of time may be removed. If this method
|
||||
* always returns {@code false}, the the related session may never
|
||||
* be stored.
|
||||
*
|
||||
* <p>This method may be called from any thread, so this call
|
||||
* must be thread safe.</p>
|
||||
*
|
||||
* @return true if active
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Return whether this session should be persisted.
|
||||
*
|
||||
* @return true if persistent
|
||||
*/
|
||||
boolean isPersistent();
|
||||
|
||||
}
|
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.sk89q.worldedit.LocalConfiguration;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent;
|
||||
import com.sk89q.worldedit.session.storage.JsonFileSessionStore;
|
||||
import com.sk89q.worldedit.session.storage.SessionStore;
|
||||
import com.sk89q.worldedit.session.storage.VoidStore;
|
||||
import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors;
|
||||
import com.sk89q.worldedit.util.eventbus.Subscribe;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Session manager for WorldEdit.
|
||||
*
|
||||
* <p>Get a reference to one from {@link WorldEdit}.</p>
|
||||
*
|
||||
* <p>While this class is thread-safe, the returned session may not be.</p>
|
||||
*/
|
||||
public class SessionManager {
|
||||
|
||||
public static int EXPIRATION_GRACE = 600000;
|
||||
private static final int FLUSH_PERIOD = 1000 * 30;
|
||||
private static final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 5));
|
||||
private static final Logger log = Logger.getLogger(SessionManager.class.getCanonicalName());
|
||||
private final Timer timer = new Timer();
|
||||
private final WorldEdit worldEdit;
|
||||
private final Map<UUID, SessionHolder> sessions = new HashMap<UUID, SessionHolder>();
|
||||
private SessionStore store = new VoidStore();
|
||||
|
||||
/**
|
||||
* Create a new session manager.
|
||||
*
|
||||
* @param worldEdit a WorldEdit instance
|
||||
*/
|
||||
public SessionManager(WorldEdit worldEdit) {
|
||||
checkNotNull(worldEdit);
|
||||
this.worldEdit = worldEdit;
|
||||
|
||||
worldEdit.getEventBus().register(this);
|
||||
timer.schedule(new SessionTracker(), FLUSH_PERIOD, FLUSH_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a session exists for the given owner.
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return true if a session exists
|
||||
*/
|
||||
public synchronized boolean contains(SessionOwner owner) {
|
||||
checkNotNull(owner);
|
||||
return sessions.containsKey(getKey(owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a session by its name specified by {@link SessionKey#getName()}.
|
||||
*
|
||||
* @param name the name
|
||||
* @return the session, if found, otherwise {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public synchronized LocalSession findByName(String name) {
|
||||
checkNotNull(name);
|
||||
for (SessionHolder holder : sessions.values()) {
|
||||
String test = holder.key.getName();
|
||||
if (test != null && name.equals(test)) {
|
||||
return holder.session;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session for an owner and return it if it exists, otherwise
|
||||
* return {@code null}.
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return the session for the owner, if it exists
|
||||
*/
|
||||
@Nullable
|
||||
public synchronized LocalSession getIfPresent(SessionOwner owner) {
|
||||
checkNotNull(owner);
|
||||
SessionHolder stored = sessions.get(getKey(owner));
|
||||
if (stored != null) {
|
||||
return stored.session;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session for an owner and create one if one doesn't exist.
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return a session
|
||||
*/
|
||||
public synchronized LocalSession get(SessionOwner owner) {
|
||||
checkNotNull(owner);
|
||||
|
||||
LocalSession session = getIfPresent(owner);
|
||||
LocalConfiguration config = worldEdit.getConfiguration();
|
||||
SessionKey sessionKey = owner.getSessionKey();
|
||||
|
||||
// No session exists yet -- create one
|
||||
if (session == null) {
|
||||
try {
|
||||
session = store.load(getKey(sessionKey));
|
||||
session.postLoad();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to load saved session", e);
|
||||
session = new LocalSession();
|
||||
}
|
||||
|
||||
session.setConfiguration(config);
|
||||
session.setBlockChangeLimit(config.defaultChangeLimit);
|
||||
|
||||
// Remember the session if the session is still active
|
||||
if (sessionKey.isActive()) {
|
||||
sessions.put(getKey(owner), new SessionHolder(sessionKey, session));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the limit on the number of blocks that an operation can
|
||||
// change at once, or don't if the owner has an override or there
|
||||
// is no limit. There is also a default limit
|
||||
int currentChangeLimit = session.getBlockChangeLimit();
|
||||
|
||||
if (!owner.hasPermission("worldedit.limit.unrestricted") && config.maxChangeLimit > -1) {
|
||||
// If the default limit is infinite but there is a maximum
|
||||
// limit, make sure to not have it be overridden
|
||||
if (config.defaultChangeLimit < 0) {
|
||||
if (currentChangeLimit < 0 || currentChangeLimit > config.maxChangeLimit) {
|
||||
session.setBlockChangeLimit(config.maxChangeLimit);
|
||||
}
|
||||
} else {
|
||||
// Bound the change limit
|
||||
int maxChangeLimit = config.maxChangeLimit;
|
||||
if (currentChangeLimit == -1 || currentChangeLimit > maxChangeLimit) {
|
||||
session.setBlockChangeLimit(maxChangeLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Have the session use inventory if it's enabled and the owner
|
||||
// doesn't have an override
|
||||
session.setUseInventory(config.useInventory
|
||||
&& !(config.useInventoryOverride
|
||||
&& (owner.hasPermission("worldedit.inventory.unrestricted")
|
||||
|| (config.useInventoryCreativeOverride && (!(owner instanceof Player) || ((Player) owner).hasCreativeMode())))));
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a map of sessions to disk.
|
||||
*
|
||||
* @param sessions a map of sessions to save
|
||||
* @return a future that completes on save or error
|
||||
*/
|
||||
private ListenableFuture<?> commit(final Map<SessionKey, LocalSession> sessions) {
|
||||
checkNotNull(sessions);
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
return Futures.immediateFuture(sessions);
|
||||
}
|
||||
|
||||
return executorService.submit(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
Exception exception = null;
|
||||
|
||||
for (Map.Entry<SessionKey, LocalSession> entry : sessions.entrySet()) {
|
||||
SessionKey key = entry.getKey();
|
||||
|
||||
if (key.isPersistent()) {
|
||||
try {
|
||||
store.save(getKey(key), entry.getValue());
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to write session for UUID " + getKey(key), e);
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return sessions;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key to use in the map for an owner.
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return the key object
|
||||
*/
|
||||
protected UUID getKey(SessionOwner owner) {
|
||||
return getKey(owner.getSessionKey());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the key to use in the map for a {@code SessionKey}.
|
||||
*
|
||||
* @param key the session key object
|
||||
* @return the key object
|
||||
*/
|
||||
protected UUID getKey(SessionKey key) {
|
||||
String forcedKey = System.getProperty("worldedit.session.uuidOverride");
|
||||
if (forcedKey != null) {
|
||||
return UUID.fromString(forcedKey);
|
||||
} else {
|
||||
return key.getUniqueId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the session for the given owner if one exists.
|
||||
*
|
||||
* @param owner the owner
|
||||
*/
|
||||
public synchronized void remove(SessionOwner owner) {
|
||||
checkNotNull(owner);
|
||||
sessions.remove(getKey(owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all sessions.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
sessions.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigurationLoad(ConfigurationLoadEvent event) {
|
||||
LocalConfiguration config = event.getConfiguration();
|
||||
File dir = new File(config.getWorkingDirectory(), "sessions");
|
||||
store = new JsonFileSessionStore(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the owner of a session, the session, and the last active time.
|
||||
*/
|
||||
private static class SessionHolder {
|
||||
private final SessionKey key;
|
||||
private final LocalSession session;
|
||||
private long lastActive = System.currentTimeMillis();
|
||||
|
||||
private SessionHolder(SessionKey key, LocalSession session) {
|
||||
this.key = key;
|
||||
this.session = session;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes inactive sessions after they have been inactive for a period
|
||||
* of time. Commits them as well.
|
||||
*/
|
||||
private class SessionTracker extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (SessionManager.this) {
|
||||
long now = System.currentTimeMillis();
|
||||
Iterator<SessionHolder> it = sessions.values().iterator();
|
||||
Map<SessionKey, LocalSession> saveQueue = new HashMap<SessionKey, LocalSession>();
|
||||
|
||||
while (it.hasNext()) {
|
||||
SessionHolder stored = it.next();
|
||||
if (stored.key.isActive()) {
|
||||
stored.lastActive = now;
|
||||
|
||||
if (stored.session.compareAndResetDirty()) {
|
||||
saveQueue.put(stored.key, stored.session);
|
||||
}
|
||||
} else {
|
||||
if (now - stored.lastActive > EXPIRATION_GRACE) {
|
||||
if (stored.session.compareAndResetDirty()) {
|
||||
saveQueue.put(stored.key, stored.session);
|
||||
}
|
||||
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveQueue.isEmpty()) {
|
||||
commit(saveQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
import com.sk89q.worldedit.util.auth.Subject;
|
||||
|
||||
/**
|
||||
* An object that owns a session.
|
||||
*/
|
||||
public interface SessionOwner extends Subject {
|
||||
|
||||
/**
|
||||
* Get an object describing this session.
|
||||
*
|
||||
* @return the status object
|
||||
*/
|
||||
SessionKey getSessionKey();
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session;
|
||||
|
||||
/**
|
||||
* Thrown if the session cannot be persisted
|
||||
* (because {@link SessionKey#isPersistent()} returns false).
|
||||
*/
|
||||
public class TransientSessionException extends Exception {
|
||||
|
||||
public TransientSessionException() {
|
||||
}
|
||||
|
||||
public TransientSessionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TransientSessionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TransientSessionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session.request;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Describes the current request using a {@link ThreadLocal}.
|
||||
*/
|
||||
public final class Request {
|
||||
|
||||
private static final ThreadLocal<Request> threadLocal =
|
||||
new ThreadLocal<Request>() {
|
||||
@Override protected Request initialValue() {
|
||||
return new Request();
|
||||
}
|
||||
};
|
||||
|
||||
private @Nullable World world;
|
||||
private @Nullable LocalSession session;
|
||||
private @Nullable EditSession editSession;
|
||||
|
||||
private Request() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request world.
|
||||
*
|
||||
* @return the world, which may be null
|
||||
*/
|
||||
public @Nullable World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request world.
|
||||
*
|
||||
* @param world the world, which may be null
|
||||
*/
|
||||
public void setWorld(@Nullable World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request session.
|
||||
*
|
||||
* @return the session, which may be null
|
||||
*/
|
||||
public @Nullable LocalSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request session.
|
||||
*
|
||||
* @param session the session, which may be null
|
||||
*/
|
||||
public void setSession(@Nullable LocalSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link EditSession}.
|
||||
*
|
||||
* @return the edit session, which may be null
|
||||
*/
|
||||
public @Nullable EditSession getEditSession() {
|
||||
return editSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link EditSession}.
|
||||
*
|
||||
* @param editSession the edit session, which may be null
|
||||
*/
|
||||
public void setEditSession(@Nullable EditSession editSession) {
|
||||
this.editSession = editSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current request, which is specific to the current thread.
|
||||
*
|
||||
* @return the current request
|
||||
*/
|
||||
public static Request request() {
|
||||
return threadLocal.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current request and clear all fields.
|
||||
*/
|
||||
public static void reset() {
|
||||
threadLocal.remove();
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session.request;
|
||||
|
||||
import com.sk89q.worldedit.*;
|
||||
import com.sk89q.worldedit.regions.NullRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.regions.RegionOperationException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A region that mirrors the current selection according to the current
|
||||
* {@link LocalSession} and {@link LocalWorld} set on the current
|
||||
* {@link Request}.
|
||||
*
|
||||
* <p>If a selection cannot be taken, then the selection will be assumed to be
|
||||
* that of a {@link NullRegion}.</p>
|
||||
*/
|
||||
public class RequestSelection implements Region {
|
||||
|
||||
/**
|
||||
* Get the delegate region.
|
||||
*
|
||||
* @return the delegate region
|
||||
*/
|
||||
protected Region getRegion() {
|
||||
LocalSession session = Request.request().getSession();
|
||||
World world = Request.request().getWorld();
|
||||
|
||||
if (session != null && world != null) {
|
||||
try {
|
||||
return session.getSelection(world);
|
||||
} catch (IncompleteRegionException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return new NullRegion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMinimumPoint() {
|
||||
return getRegion().getMinimumPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMaximumPoint() {
|
||||
return getRegion().getMaximumPoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getCenter() {
|
||||
return getRegion().getCenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArea() {
|
||||
return getRegion().getArea();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return getRegion().getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return getRegion().getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLength() {
|
||||
return getRegion().getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expand(Vector... changes) throws RegionOperationException {
|
||||
getRegion().expand(changes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contract(Vector... changes) throws RegionOperationException {
|
||||
getRegion().contract(changes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shift(Vector change) throws RegionOperationException {
|
||||
getRegion().shift(change);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Vector position) {
|
||||
return getRegion().contains(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Vector2D> getChunks() {
|
||||
return getRegion().getChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Vector> getChunkCubes() {
|
||||
return getRegion().getChunkCubes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return getRegion().getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorld(LocalWorld world) {
|
||||
setWorld((World) world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorld(World world) {
|
||||
getRegion().setWorld(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Region clone() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockVector2D> polygonize(int maxPoints) {
|
||||
return getRegion().polygonize(maxPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<BlockVector> iterator() {
|
||||
return getRegion().iterator();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session.storage;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
import com.sk89q.worldedit.util.gson.GsonUtil;
|
||||
import com.sk89q.worldedit.util.io.Closer;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores sessions as JSON files in a directory.
|
||||
*
|
||||
* <p>Currently, this implementation doesn't handle thread safety very well.</p>
|
||||
*/
|
||||
public class JsonFileSessionStore implements SessionStore {
|
||||
|
||||
private static final Logger log = Logger.getLogger(JsonFileSessionStore.class.getCanonicalName());
|
||||
private final Gson gson;
|
||||
private final File dir;
|
||||
|
||||
/**
|
||||
* Create a new session store.
|
||||
*
|
||||
* @param dir the directory
|
||||
*/
|
||||
public JsonFileSessionStore(File dir) {
|
||||
checkNotNull(dir);
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
if (!dir.mkdirs()) {
|
||||
log.log(Level.WARNING, "Failed to create directory '" + dir.getPath() + "' for sessions");
|
||||
}
|
||||
}
|
||||
|
||||
this.dir = dir;
|
||||
|
||||
GsonBuilder builder = GsonUtil.createBuilder();
|
||||
gson = builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path for the given UUID.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return the file
|
||||
*/
|
||||
private File getPath(UUID id) {
|
||||
checkNotNull(id);
|
||||
return new File(dir, id + ".json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalSession load(UUID id) throws IOException {
|
||||
File file = getPath(id);
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
FileReader fr = closer.register(new FileReader(file));
|
||||
BufferedReader br = closer.register(new BufferedReader(fr));
|
||||
return gson.fromJson(br, LocalSession.class);
|
||||
} catch (JsonParseException e) {
|
||||
throw new IOException(e);
|
||||
} catch (FileNotFoundException e) {
|
||||
return new LocalSession();
|
||||
} finally {
|
||||
try {
|
||||
closer.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(UUID id, LocalSession session) throws IOException {
|
||||
File finalFile = getPath(id);
|
||||
File tempFile = new File(finalFile.getParentFile(), finalFile.getName() + ".tmp");
|
||||
Closer closer = Closer.create();
|
||||
|
||||
try {
|
||||
FileWriter fr = closer.register(new FileWriter(tempFile));
|
||||
BufferedWriter bw = closer.register(new BufferedWriter(fr));
|
||||
gson.toJson(session, bw);
|
||||
} catch (JsonIOException e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
try {
|
||||
closer.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (finalFile.exists()) {
|
||||
if (!finalFile.delete()) {
|
||||
log.log(Level.WARNING, "Failed to delete " + finalFile.getPath() + " so the .tmp file can replace it");
|
||||
}
|
||||
}
|
||||
|
||||
if (!tempFile.renameTo(finalFile)) {
|
||||
log.log(Level.WARNING, "Failed to rename temporary session file to " + finalFile.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session.storage;
|
||||
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Commits sessions to disk.
|
||||
*
|
||||
* <p>Both {@link #load(UUID)} and {@link #save(UUID, LocalSession)} may be
|
||||
* called at the same in different threads, so implementations should
|
||||
* be aware of this issue.</p>
|
||||
*/
|
||||
public interface SessionStore {
|
||||
|
||||
/**
|
||||
* Load a session identified by the given UUID.
|
||||
*
|
||||
* <p>If the session does not exist (has never been saved), then
|
||||
* a new {@link LocalSession} must be returned.</p>
|
||||
*
|
||||
* @param id the UUID
|
||||
* @return a session
|
||||
* @throws IOException thrown on read error
|
||||
*/
|
||||
LocalSession load(UUID id) throws IOException;
|
||||
|
||||
/**
|
||||
* Save the given session identified by the given UUID.
|
||||
*
|
||||
* @param id the UUID
|
||||
* @param session a session
|
||||
* @throws IOException thrown on read error
|
||||
*/
|
||||
void save(UUID id, LocalSession session) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.session.storage;
|
||||
|
||||
import com.sk89q.worldedit.LocalSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A session store that doesn't know how to store sessions.
|
||||
*/
|
||||
public class VoidStore implements SessionStore {
|
||||
|
||||
@Override
|
||||
public LocalSession load(UUID id) throws IOException {
|
||||
return new LocalSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(UUID id, LocalSession session) throws IOException {
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user