diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java
new file mode 100644
index 0000000000..0952a2caa9
--- /dev/null
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java
@@ -0,0 +1,106 @@
+package org.bukkit.craftbukkit.scheduler;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.CancellationException;
+
+public class CraftFuture<T> implements Runnable, Future<T> {
+
+    private final CraftScheduler craftScheduler;
+    private final Callable<T> callable;
+    private final ObjectContainer<T> returnStore = new ObjectContainer<T>();
+    private boolean done = false;
+    private boolean running = false;
+    private boolean cancelled = false;
+    private Exception e = null;
+    private int taskId = -1;
+
+    CraftFuture(CraftScheduler craftScheduler, Callable callable) {
+        this.callable = callable;
+        this.craftScheduler = craftScheduler;
+    }
+
+    public void run() {
+        synchronized(this) {
+            if(cancelled) {
+                return;
+            }
+            running = true;
+        }
+        try {
+            returnStore.setObject(callable.call());
+        } catch (Exception e) {
+            this.e = e;
+        }
+        synchronized(this) {
+            running = false;
+            done = true;
+            this.notify();
+        }
+    }
+
+    public T get() throws InterruptedException, ExecutionException {
+        try {
+            return get(0L, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException te) {}
+        return null;
+    }
+
+    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+        synchronized(this) {
+            if(isDone()) {
+                return getResult();
+            }
+            this.wait(TimeUnit.MILLISECONDS.convert(timeout, unit));
+            return getResult();
+        }
+    }
+
+    public T getResult() throws ExecutionException {
+        if(cancelled) {
+            throw new CancellationException();
+        }
+        if(e!=null) {
+            throw new ExecutionException(e);
+        }
+        return returnStore.getObject();
+    }
+
+    public boolean isDone() {
+        synchronized(this) {
+            return done;
+        }
+    }
+
+    public boolean isCancelled() {
+        synchronized(this) {
+            return cancelled;
+        }
+    }
+
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        synchronized(this) {
+            if(cancelled) {
+                return false;
+            }
+            cancelled = true;
+            if(taskId!=-1) {
+                craftScheduler.cancelTask(taskId);
+            }
+            if(!running && !done) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public void setTaskId(int taskId) {
+        synchronized(this) {
+            this.taskId = taskId;
+        }
+    }
+}
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
index edf5bae8d2..46e095bc88 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -6,6 +6,8 @@ import java.util.Iterator;
 
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
 
 import org.bukkit.scheduler.BukkitScheduler;
 import org.bukkit.plugin.Plugin;
@@ -105,7 +107,6 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
 
     }
 
-
     // If the main thread cannot obtain the lock, it doesn't wait
     public void mainThreadHeartbeat(long currentTick) {
         if (mainThreadLock.tryLock()) {
@@ -177,6 +178,15 @@ public class CraftScheduler implements BukkitScheduler, Runnable {
         return newTask.getIdNumber();
     }
 
+    public <T> Future<T> callSyncMethod(Plugin plugin, Callable<T> task) {
+        CraftFuture<T> craftFuture = new CraftFuture<T>(this, task);
+        synchronized(craftFuture) {
+            int taskId = scheduleSyncDelayedTask(plugin, craftFuture);
+            craftFuture.setTaskId(taskId);
+        }
+        return craftFuture;
+    }
+
     public void cancelTask(int taskId) {
         synchronized (schedulerQueue) {
             Iterator<CraftTask> itr = schedulerQueue.keySet().iterator();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/ObjectContainer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/ObjectContainer.java
new file mode 100644
index 0000000000..c0fa37cb11
--- /dev/null
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/scheduler/ObjectContainer.java
@@ -0,0 +1,15 @@
+package org.bukkit.craftbukkit.scheduler;
+
+public class ObjectContainer<T> {
+
+    T object;
+
+    public void setObject(T object) {
+        this.object = object;
+    }
+
+    public T getObject() {
+        return object;
+    }
+
+}