diff --git a/event-loop/TODO b/event-loop/TODO deleted file mode 100644 index 373999f..0000000 --- a/event-loop/TODO +++ /dev/null @@ -1,7 +0,0 @@ -- Система иерархических блокировок ресурсов (чанки в мире) - - Нужно что-то делать с подгрузкой отсутсвующих чанков для таких блокировок -- Возможность вызвать событие из EventHandler -- Performance Monitor -- Возможная проблема с переполнением очереди при спаме пакетами от игрока -- Добавить поля с замками для ресурсов (Player, World, Chunk) -- Time Scheduler \ No newline at end of file diff --git a/event-loop/build.gradle b/event-loop/build.gradle deleted file mode 100644 index 0a1c7d0..0000000 --- a/event-loop/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -group 'mc' -version '1.0-SNAPSHOT' - -dependencies { - /* Core */ - compile_excludeCopy project(':core') - - testCompile group: 'org.slf4j', name: 'slf4j-simple', version: '1.6.1' - testCompile group: 'com.carrotsearch', name: 'junit-benchmarks', version: '0.7.0' -} - - -test { - exclude "ru/core/events/*Benchmark.class" -} \ No newline at end of file diff --git a/event-loop/src/main/java/mc/core/events/EventPipelineTask.java b/event-loop/src/main/java/mc/core/events/EventPipelineTask.java deleted file mode 100644 index f79634f..0000000 --- a/event-loop/src/main/java/mc/core/events/EventPipelineTask.java +++ /dev/null @@ -1,113 +0,0 @@ -package mc.core.events; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import mc.core.events.api.EventQueueOwner; -import mc.core.events.api.LockableResource; -import mc.core.events.runner.lock.LockObserveList; -import mc.core.events.runner.ResourceAwareExecutorService; -import mc.core.events.runner.ResourceAwareRunnable; - -import java.lang.reflect.InvocationTargetException; -import java.util.List; - -/** - * Holds processing pipeline for every event - * that enters {@link FullAsyncEventLoop}. - *

- * Ensures that EventHandlers will never be called in a wrong - * order by feeding only one task at a time to the {@link ResourceAwareExecutorService} - */ -@RequiredArgsConstructor -@Getter -@Slf4j -public class EventPipelineTask { - private final ResourceAwareExecutorService service; - private final List handlers; - private final FullAsyncEventLoop manager; - private final Event event; - private final EventQueueOwner owner; - private int currentIndex = 0; - @Setter - private PipelineState state = PipelineState.IDLE; - - public void next() { - if (updatePipelineState()) return; - - RegisteredEventHandler handler = handlers.get(currentIndex); - // If event has been already cancelled and current handler - // ignores cancelled events - if (event.isCanceled() && handler.isIgnoreCancelled()) { - // Just skip current event handler - currentIndex++; - next(); - } else { - feedTask(handler); - } - } - - /** - * Update current pipeline status - * - * @return true if pipeline has been completed - */ - private boolean updatePipelineState() { - if (state == PipelineState.IDLE) { - state = PipelineState.WORKING; - } - if (currentIndex >= handlers.size() && state == PipelineState.WORKING) { - state = PipelineState.FINISHED; - manager.update(owner); - return true; - } - - if (state == PipelineState.FINISHED) { - throw new IllegalStateException("Attempted to call next step on a FINISHED pipeline"); - } - return false; - } - - private void feedTask(RegisteredEventHandler handler) { - LockObserveList locks = getLocks(handler); - service.addTask(new ResourceAwareRunnable() { - @Override - public void run() { - try { - handler.getMethod().invoke(handler.getObject(), event); - } catch (IllegalAccessException | InvocationTargetException e) { - log.error("Unable to dispatch event " + event.getClass().getSimpleName() + " to handler " + event.getClass().getName(), e); - } - } - - @Override - public void after() { - currentIndex++; - next(); - } - - @Override - public LockObserveList getLocks() { - return locks; - } - }); - } - - private LockObserveList getLocks(RegisteredEventHandler handler) { - LockObserveList locks = new LockObserveList(); - - if (handler.isPluginSynchronize()) - locks.add(manager.getResourceManager().getPluginLock(handler.getPlugin())); - - for (LockableResource resource : handler.getLock()) { - locks.addAll(manager.getResourceManager().getAnnotationLocks(resource, event)); - } - - return locks; - } - - public enum PipelineState { - IDLE, WORKING, FINISHED - } -} diff --git a/event-loop/src/main/java/mc/core/events/FullAsyncEventLoop.java b/event-loop/src/main/java/mc/core/events/FullAsyncEventLoop.java deleted file mode 100644 index ec8fa85..0000000 --- a/event-loop/src/main/java/mc/core/events/FullAsyncEventLoop.java +++ /dev/null @@ -1,121 +0,0 @@ -package mc.core.events; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import mc.core.events.api.EventHandler; -import mc.core.events.api.EventQueueOwner; -import mc.core.events.api.Plugin; -import mc.core.events.runner.ResourceAwareExecutorService; -import org.springframework.beans.factory.annotation.Autowired; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Event loop core. Manages event handler registration process, - * maintains event queues. - *

- * This event loop guarantees that events, assigned to the {@link EventQueueOwner} - * will be handler in order of scheduling - */ -@Slf4j -public class FullAsyncEventLoop { - // Item leaves this queue only when EventPipeline is fully executed - private Map> eventQueue = new ConcurrentHashMap<>(); - private Map, List> registeredHandlers = new HashMap<>(); - @SuppressWarnings("SpringJavaAutowiredMembersInspection") - @Autowired - @Setter - private ResourceAwareExecutorService resourceAwareExecutorService; - @Getter - private SharedResourceManager resourceManager = new SharedResourceManager(); - - public void addEventHandler(Plugin plugin, Object object) { - Map candidates = getEventHandlerCandidates(object); - - for (Map.Entry pair : candidates.entrySet()) { - @SuppressWarnings("unchecked") Class eventType = (Class) pair.getKey().getParameterTypes()[0]; - List handlers = this.registeredHandlers.computeIfAbsent(eventType, e -> new ArrayList<>()); - handlers.add(new RegisteredEventHandler(plugin, object, pair.getKey(), pair.getValue().lock(), pair.getValue().pluginSynchronize(), pair.getValue().priority().getValue(), pair.getValue().ignoreCancelled())); - handlers.sort(Comparator.comparingInt(RegisteredEventHandler::getPriority)); - } - } - - public List getPipelineForEvent(Event event) { - return registeredHandlers.get(event.getClass()); - } - - private Map getEventHandlerCandidates(Object object) { - Map candidates; - candidates = new HashMap<>(); - for (Method method : object.getClass().getDeclaredMethods()) { - EventHandler annotation = method.getAnnotation(EventHandler.class); - if (annotation == null) - continue; - - if (!Modifier.isPublic(method.getModifiers())) { - log.error("Unable to register {} as an EventHandler. Method must have a 'public' access modifier.", method.toString()); - continue; - - } - - if (method.getParameterCount() != 1) { - log.error("Unable to register {} as an EventHandler. Method must have exactly one argument.", method.toString()); - continue; - } - - Class firstParamType = method.getParameterTypes()[0]; - if (!Event.class.isAssignableFrom(firstParamType)) { - log.error("Unable to register {} as an EventHandler. First parameter type must implement 'Event' interface.", method.toString()); - continue; - } - - method.setAccessible(true); - candidates.put(method, annotation); - } - return candidates; - } - - public void asyncFireEvent(EventQueueOwner owner, Event event) { - List handlers = getPipelineForEvent(event); - if (handlers == null) - return; - - Queue queue = eventQueue.computeIfAbsent(owner, s -> new ArrayDeque<>()); - queue.add(new EventPipelineTask(resourceAwareExecutorService, handlers, this, event, owner)); - update(owner); - } - - /** - * Updates queue state for a given owner: - *

- * - Removes first element of a queue if it is marked as FINISHED - * - Starts executing first pipeline from the queue if it is marked with IDLE - * - * @param owner queue owner - */ - public synchronized void update(EventQueueOwner owner) { - if (!eventQueue.containsKey(owner)) { - log.warn("Unable to update pipeline executor: unable to find queue"); - return; - } - Queue queue = eventQueue.get(owner); - if (queue.isEmpty()) { - log.warn("Unable to update pipeline executor: queue is empty"); - return; - } - - if (queue.peek().getState() == EventPipelineTask.PipelineState.FINISHED) { - queue.poll(); - } - - EventPipelineTask pipeline; - if ((pipeline = queue.peek()) != null - && pipeline.getState() == EventPipelineTask.PipelineState.IDLE) { - pipeline.next(); - } - } -} diff --git a/event-loop/src/main/java/mc/core/events/RegisteredEventHandler.java b/event-loop/src/main/java/mc/core/events/RegisteredEventHandler.java deleted file mode 100644 index f0333f7..0000000 --- a/event-loop/src/main/java/mc/core/events/RegisteredEventHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package mc.core.events; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import mc.core.events.api.LockableResource; -import mc.core.events.api.Plugin; - -import java.lang.reflect.Method; - -/** - * Holds all the information necessary to register an - * event handler in an event loop - */ -@RequiredArgsConstructor -@Getter -public class RegisteredEventHandler { - private final Plugin plugin; - private final Object object; - private final Method method; - private final LockableResource[] lock; - private final boolean pluginSynchronize; - private final int priority; - private final boolean ignoreCancelled; -} diff --git a/event-loop/src/main/java/mc/core/events/SharedResourceManager.java b/event-loop/src/main/java/mc/core/events/SharedResourceManager.java deleted file mode 100644 index ad9f55e..0000000 --- a/event-loop/src/main/java/mc/core/events/SharedResourceManager.java +++ /dev/null @@ -1,64 +0,0 @@ -package mc.core.events; - -import lombok.extern.slf4j.Slf4j; -import mc.core.Location; -import mc.core.events.api.LockableResource; -import mc.core.events.api.Plugin; -import mc.core.events.api.interfaces.LocationProvidingEvent; -import mc.core.events.api.interfaces.PlayerProvidingEvent; -import mc.core.events.api.interfaces.WorldProvidingEvent; -import mc.core.events.runner.lock.PoorMansLock; -import mc.core.player.Player; -import mc.core.world.World; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -@Slf4j -public class SharedResourceManager { - private Map pluginLocks = new ConcurrentHashMap<>(); - // TODO: Memory leak HERE. Fix with introducing field to Player class - private Map playerLocks = new ConcurrentHashMap<>(); - // TODO: Memory leak HERE. Fix with introducing field to World class - private Map worldLocks = new ConcurrentHashMap<>(); - - public PoorMansLock getPluginLock(Plugin plugin) { - return pluginLocks.computeIfAbsent(plugin, s -> new PoorMansLock()); - } - - public PoorMansLock getPlayerLock(Player player) { - return playerLocks.computeIfAbsent(player, s -> new PoorMansLock()); - } - - public PoorMansLock getWorldLock(World world) { - return worldLocks.computeIfAbsent(world, s -> new PoorMansLock()); - } - - private T require(LockableResource resource, Event event, Class inter) { - if (inter.isInstance(event)) { - //noinspection unchecked - return (T) event; - } else - throw new IllegalArgumentException("Unable to lock " + resource + " while attempting to process event. Event " + event.getClass().getSimpleName() + " must implement " + inter); - } - - public Collection getAnnotationLocks(LockableResource resource, Event event) { - switch (resource) { - case PLAYER: - return require(resource, event, PlayerProvidingEvent.class).getAssociatedPlayers().stream().map(this::getPlayerLock).collect(Collectors.toList()); - case PLAYER_WORLD: - return require(resource, event, PlayerProvidingEvent.class).getAssociatedPlayers().stream().map(s -> s.getLocation().getWorld()).map(this::getWorldLock).collect(Collectors.toList()); - case EVENT_LOCATION_WORLD: - return require(resource, event, LocationProvidingEvent.class).getAssociatedLocations().stream().map(Location::getWorld).map(this::getWorldLock).collect(Collectors.toList()); - case EVENT_WORLD: - return require(resource, event, WorldProvidingEvent.class).getAssociatedWorlds().stream().map(this::getWorldLock).collect(Collectors.toList()); - default: - log.warn("Unable to find action for " + resource + " resource definition."); - return Collections.emptyList(); - } - } - -} diff --git a/event-loop/src/main/java/mc/core/events/api/EventHandler.java b/event-loop/src/main/java/mc/core/events/api/EventHandler.java deleted file mode 100644 index dbf255d..0000000 --- a/event-loop/src/main/java/mc/core/events/api/EventHandler.java +++ /dev/null @@ -1,17 +0,0 @@ -package mc.core.events.api; - -import java.lang.annotation.*; - -@Documented -@Target(ElementType.METHOD) -@Inherited -@Retention(RetentionPolicy.RUNTIME) -public @interface EventHandler { - EventPriority priority() default EventPriority.NORMAL; - - boolean ignoreCancelled() default false; - - boolean pluginSynchronize() default true; - - LockableResource[] lock() default {}; -} diff --git a/event-loop/src/main/java/mc/core/events/api/EventPriority.java b/event-loop/src/main/java/mc/core/events/api/EventPriority.java deleted file mode 100644 index 0c6fdce..0000000 --- a/event-loop/src/main/java/mc/core/events/api/EventPriority.java +++ /dev/null @@ -1,19 +0,0 @@ -package mc.core.events.api; - -import lombok.Getter; - -public enum EventPriority { - LOWEST(0), - LOW(1), - NORMAL(2), - HIGH(3), - HIGHEST(4), - MONITOR(5); - - @Getter - private int value; - - EventPriority(int value) { - this.value = value; - } -} diff --git a/event-loop/src/main/java/mc/core/events/api/EventQueueOwner.java b/event-loop/src/main/java/mc/core/events/api/EventQueueOwner.java deleted file mode 100644 index 49f4fd4..0000000 --- a/event-loop/src/main/java/mc/core/events/api/EventQueueOwner.java +++ /dev/null @@ -1,4 +0,0 @@ -package mc.core.events.api; - -public interface EventQueueOwner { -} diff --git a/event-loop/src/main/java/mc/core/events/api/LockableResource.java b/event-loop/src/main/java/mc/core/events/api/LockableResource.java deleted file mode 100644 index 5b86b0a..0000000 --- a/event-loop/src/main/java/mc/core/events/api/LockableResource.java +++ /dev/null @@ -1,10 +0,0 @@ -package mc.core.events.api; - -public enum LockableResource { - PLAYER, - PLAYER_WORLD, - EVENT_LOCATION_WORLD, - EVENT_WORLD - - // TODO: Add entity-related constants -} diff --git a/event-loop/src/main/java/mc/core/events/api/Plugin.java b/event-loop/src/main/java/mc/core/events/api/Plugin.java deleted file mode 100644 index 040ab60..0000000 --- a/event-loop/src/main/java/mc/core/events/api/Plugin.java +++ /dev/null @@ -1,4 +0,0 @@ -package mc.core.events.api; - -public interface Plugin { -} diff --git a/event-loop/src/main/java/mc/core/events/api/interfaces/LocationProvidingEvent.java b/event-loop/src/main/java/mc/core/events/api/interfaces/LocationProvidingEvent.java deleted file mode 100644 index bec7040..0000000 --- a/event-loop/src/main/java/mc/core/events/api/interfaces/LocationProvidingEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package mc.core.events.api.interfaces; - -import mc.core.Location; - -import java.util.Collection; - -public interface LocationProvidingEvent { - Collection getAssociatedLocations(); -} diff --git a/event-loop/src/main/java/mc/core/events/api/interfaces/PlayerProvidingEvent.java b/event-loop/src/main/java/mc/core/events/api/interfaces/PlayerProvidingEvent.java deleted file mode 100644 index d05af39..0000000 --- a/event-loop/src/main/java/mc/core/events/api/interfaces/PlayerProvidingEvent.java +++ /dev/null @@ -1,21 +0,0 @@ -package mc.core.events.api.interfaces; - -import mc.core.Location; -import mc.core.player.Player; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -public interface PlayerProvidingEvent extends LocationProvidingEvent { - List getAssociatedPlayers(); - - @Override - default Collection getAssociatedLocations() { - List players = getAssociatedPlayers(); - if (players.size() == 1) - return Collections.singletonList(players.get(0).getLocation()); - else - throw new RuntimeException("This method is not implemented."); - } -} diff --git a/event-loop/src/main/java/mc/core/events/api/interfaces/WorldProvidingEvent.java b/event-loop/src/main/java/mc/core/events/api/interfaces/WorldProvidingEvent.java deleted file mode 100644 index 9f561e5..0000000 --- a/event-loop/src/main/java/mc/core/events/api/interfaces/WorldProvidingEvent.java +++ /dev/null @@ -1,10 +0,0 @@ -package mc.core.events.api.interfaces; - -import mc.core.world.World; - -import java.util.Collection; - -public interface WorldProvidingEvent { - Collection getAssociatedWorlds(); - -} diff --git a/event-loop/src/main/java/mc/core/events/api/samples/BlockBreakEvent.java b/event-loop/src/main/java/mc/core/events/api/samples/BlockBreakEvent.java deleted file mode 100644 index a515022..0000000 --- a/event-loop/src/main/java/mc/core/events/api/samples/BlockBreakEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -package mc.core.events.api.samples; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import mc.core.Location; -import mc.core.events.EventBase; -import mc.core.events.api.interfaces.LocationProvidingEvent; -import mc.core.events.api.interfaces.PlayerProvidingEvent; -import mc.core.player.Player; -import mc.core.world.block.Block; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -@RequiredArgsConstructor -@Getter -public class BlockBreakEvent extends EventBase implements PlayerProvidingEvent, LocationProvidingEvent { - private final Player player; - private final Block block; - - @Override - public List getAssociatedPlayers() { - return Collections.singletonList(player); - } - - @Override - public Collection getAssociatedLocations() { - return Collections.singletonList(block.getLocation()); - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/AllInScheduleStrategy.java b/event-loop/src/main/java/mc/core/events/runner/AllInScheduleStrategy.java deleted file mode 100644 index ce275bd..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/AllInScheduleStrategy.java +++ /dev/null @@ -1,59 +0,0 @@ -package mc.core.events.runner; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; - -/** - * Simple scheduling strategy. - *

- * We wait until the first task in a queue will be able to acquire all - * the necessary resources and then we schedule it for execution - */ -public class AllInScheduleStrategy implements ScheduleStrategy { - private BlockingQueue globalQueue; - private ResourceAwareExecutorService resourceAwareExecutorService; - - public AllInScheduleStrategy(ResourceAwareExecutorService resourceAwareExecutorService) { - this.globalQueue = resourceAwareExecutorService.queue; - this.resourceAwareExecutorService = resourceAwareExecutorService; - } - - - @Override - public synchronized ResourceAwareRunnable getTask() throws InterruptedException { - waitForResourceLockComplete(); - - // Wait for new task in queue - ResourceAwareRunnable runnable = globalQueue.take(); - while (!runnable.getLocks().isReady()) { - CountDownLatch latch = new CountDownLatch(1); - runnable.getLocks().setCallback(latch::countDown); - // Prevent situations where dependencies were resolved - // while we were setting up the callback - if (runnable.getLocks().isReady()) - continue; - latch.await(); - } - - // Lock execution for the next thread - // (wait until resources for previous task will be blocked) - resourceAwareExecutorService.waitForLock.set(true); - return runnable; - } - - /** - * Waits until the last scheduled task will lock all the necessary resources. - *

- * It is required to avoid race-condition when an execution candidate task (first task in a queue) - * skips lock-await procedure due to the last scheduled task not having locked necessary resources yet. - * - * @throws InterruptedException if current thread is interrupted - */ - private void waitForResourceLockComplete() throws InterruptedException { - synchronized (resourceAwareExecutorService.waitForLock) { - while (resourceAwareExecutorService.waitForLock.get()) { - resourceAwareExecutorService.wait(); - } - } - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/ExecutorWorkerThread.java b/event-loop/src/main/java/mc/core/events/runner/ExecutorWorkerThread.java deleted file mode 100644 index e1e5f7b..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/ExecutorWorkerThread.java +++ /dev/null @@ -1,55 +0,0 @@ -package mc.core.events.runner; - -/** - * Worker thread for {@link ResourceAwareExecutorService}. - *

- * - Awaits for tasks from {@link ScheduleStrategy} - * - Locks up resources for this task - * - Notifies {@link ScheduleStrategy} when resource-locking procedure is complete - * - Executes the runnable in this thread - * - Unlocks all the resources - * - Calls {@link ResourceAwareRunnable#after()} callback - */ -public class ExecutorWorkerThread extends Thread { - private ResourceAwareExecutorService service; - - public ExecutorWorkerThread(String name, ResourceAwareExecutorService service) { - super(name); - this.service = service; - } - - @Override - public void run() { - while (!isInterrupted() && isAlive()) { - ResourceAwareRunnable runnable; - try { - runnable = service.getStrategy().getTask(); - } catch (InterruptedException e) { - return; - } - - executeTask(runnable); - } - } - - void executeTask(ResourceAwareRunnable runnable) { - runnable.getLocks().lockAll(); - notifyLockingDone(); - try { - runnable.run(); - } finally { - runnable.getLocks().unlockAll(); - runnable.getLocks().release(); - } - runnable.after(); - } - - private void notifyLockingDone() { - synchronized (service.waitForLock) { - if (service.waitForLock.get()) { - service.waitForLock.set(false); - service.waitForLock.notifyAll(); - } - } - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/ResourceAwareExecutorService.java b/event-loop/src/main/java/mc/core/events/runner/ResourceAwareExecutorService.java deleted file mode 100644 index 0ec99ca..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/ResourceAwareExecutorService.java +++ /dev/null @@ -1,74 +0,0 @@ -package mc.core.events.runner; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - - -/** - * Custom implementation of an ExecutorService. - * - * Holds a queue of {@link ResourceAwareRunnable} and executes them in a thread pool. - * - * Warning! This class doesn't guarantee, that tasks will be executed in any specific order. - * In fact, it's up to {@link ScheduleStrategy} to decide which task will be scheduled for - * execution next. - */ -public class ResourceAwareExecutorService { - private static final boolean WORKER_INSTANT_EXECUTE = false; - BlockingQueue queue = new ArrayBlockingQueue<>(100); - // A synchronize aid, that prevents ScheduleStrategy from returning - // wrong tasks when executor is late in blocking resources - final AtomicBoolean waitForLock = new AtomicBoolean(false); - private ScheduleStrategy strategy = new AllInScheduleStrategy(this); - private Set executorThreads = new HashSet<>(); - private int threadCount; - - public ResourceAwareExecutorService(int threadCount) { - this.threadCount = threadCount; - } - - public void start() { - if (executorThreads.size() > 0) - throw new RuntimeException("This executor service was already started."); - - for (int i = 0; i < threadCount; i++) { - Thread thread = new ExecutorWorkerThread("Event Loop #" + i, this); - executorThreads.add(thread); - thread.start(); - } - } - - public void stop() { - if (executorThreads.size() == 0) - throw new RuntimeException("This executor service was not initialized yet."); - - Iterator iterator = executorThreads.iterator(); - while (iterator.hasNext()) { - Thread thread = iterator.next(); - thread.interrupt(); - iterator.remove(); - } - } - - public void addTask(ResourceAwareRunnable task) { - if (WORKER_INSTANT_EXECUTE && Thread.currentThread() instanceof ExecutorWorkerThread) { - ((ExecutorWorkerThread) Thread.currentThread()).executeTask(task); - } else - queue.offer(task); - } - - - public ScheduleStrategy getStrategy() { - return strategy; - } - - private class DefaultScheduleStrategy implements ScheduleStrategy { - public ResourceAwareRunnable getTask() throws InterruptedException { - return queue.take(); - } - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/ResourceAwareRunnable.java b/event-loop/src/main/java/mc/core/events/runner/ResourceAwareRunnable.java deleted file mode 100644 index 6f88476..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/ResourceAwareRunnable.java +++ /dev/null @@ -1,13 +0,0 @@ -package mc.core.events.runner; - -import mc.core.events.runner.lock.LockObserveList; - -public interface ResourceAwareRunnable extends Runnable { - default LockObserveList getLocks() { - return LockObserveList.EMPTY_LIST; - } - - default void after() { - - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/ScheduleStrategy.java b/event-loop/src/main/java/mc/core/events/runner/ScheduleStrategy.java deleted file mode 100644 index 10cee55..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/ScheduleStrategy.java +++ /dev/null @@ -1,5 +0,0 @@ -package mc.core.events.runner; - -public interface ScheduleStrategy { - ResourceAwareRunnable getTask() throws InterruptedException; -} diff --git a/event-loop/src/main/java/mc/core/events/runner/lock/LockObserveList.java b/event-loop/src/main/java/mc/core/events/runner/lock/LockObserveList.java deleted file mode 100644 index 157d325..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/lock/LockObserveList.java +++ /dev/null @@ -1,61 +0,0 @@ -package mc.core.events.runner.lock; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -public class LockObserveList implements Consumer { - public static LockObserveList EMPTY_LIST = new LockObserveList(); - private List locks = new ArrayList<>(); - private Runnable callback; - - public void setCallback(Runnable callback) { - this.callback = callback; - } - - public void add(PoorMansLock lock) { - locks.add(lock); - lock.addCallback(this); - } - - public void addAll(Iterable locks) { - for (PoorMansLock lock : locks) - add(lock); - } - - public void release() { - callback = null; - for (PoorMansLock lock : locks) { - lock.removeCallback(this); - } - locks.clear(); - } - - public boolean isReady() { - for (PoorMansLock lock : locks) { - if (lock.isLocked()) - return false; - } - return true; - } - - public void lockAll() { - for (PoorMansLock lock : locks) - lock.lock(); - } - - public void unlockAll() { - for (PoorMansLock lock : locks) - lock.unlock(); - } - - @Override - public void accept(PoorMansLock lock) { - if (!lock.isLocked()) { - if (isReady()) { - if (callback != null) - callback.run(); - } - } - } -} diff --git a/event-loop/src/main/java/mc/core/events/runner/lock/PoorMansLock.java b/event-loop/src/main/java/mc/core/events/runner/lock/PoorMansLock.java deleted file mode 100644 index ace2edc..0000000 --- a/event-loop/src/main/java/mc/core/events/runner/lock/PoorMansLock.java +++ /dev/null @@ -1,52 +0,0 @@ -package mc.core.events.runner.lock; - -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.function.Consumer; - -public class PoorMansLock { - private Thread owner = null; - private Set> callbacks = new CopyOnWriteArraySet<>(); - - public void addCallback(Consumer callback) { - callbacks.add(callback); - } - - public void removeCallback(Consumer callback) { - callbacks.remove(callback); - } - - - public boolean isLocked() { - return owner != null; - } - - private void triggerUpdate() { - for (Consumer consumer : callbacks) - consumer.accept(this); - } - - public synchronized void lock() { - if(owner == Thread.currentThread()) - return; - - if (owner != null) { - throw new RuntimeException("Unable to lock this resource: already in use"); - } - - owner = Thread.currentThread(); - triggerUpdate(); - } - - public synchronized void unlock() { - if (owner == null) - return; - - if (owner != Thread.currentThread()) { - throw new RuntimeException("Attempt to unlock resource from non-owning thread"); - } - - owner = null; - triggerUpdate(); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/ThreadTimings.java b/event-loop/src/main/java/mc/core/timings/ThreadTimings.java deleted file mode 100644 index 408842b..0000000 --- a/event-loop/src/main/java/mc/core/timings/ThreadTimings.java +++ /dev/null @@ -1,45 +0,0 @@ -package mc.core.timings; - -import java.util.Stack; -import java.util.concurrent.atomic.AtomicInteger; - -public class ThreadTimings { - private static AtomicInteger IDS = new AtomicInteger(); - private int threadId; - private Stack stack = new Stack<>(); - - public ThreadTimings() { - this.threadId = IDS.getAndIncrement(); - } - - public Stack getStack() { - return stack; - } - - public int getThreadId() { - return threadId; - } - - public Timings start() { - Timings timings = new Timings(this, stack.size()); - getTimingsManager().waitForTimingsInitialize(); - stack.push(timings); - getTimingsManager().notifyTimings(this, timings, true); - return timings; - } - - private TimingsManager getTimingsManager() { - return Timings.getTimingsManager(); - } - - public void end(Timings finished) { - Timings timings = null; - while (!stack.isEmpty() && timings != finished) { - getTimingsManager().waitForTimingsInitialize(); - timings = stack.pop(); - if (!timings.hasFinished()) - timings.finish(); - getTimingsManager().notifyTimings(this, timings, false); - } - } -} diff --git a/event-loop/src/main/java/mc/core/timings/Timings.java b/event-loop/src/main/java/mc/core/timings/Timings.java deleted file mode 100644 index b9d1252..0000000 --- a/event-loop/src/main/java/mc/core/timings/Timings.java +++ /dev/null @@ -1,51 +0,0 @@ -package mc.core.timings; - -public class Timings implements AutoCloseable { - private ThreadTimings threadTimings; - private long acquireTime; - @SuppressWarnings("FieldCanBeLocal") - private long endTime = -1; - private int id; - - public Timings(ThreadTimings threadTimings, int id) { - this.id = id; - this.threadTimings = threadTimings; - this.acquireTime = System.nanoTime(); - } - - public static Timings start() { - return TimingsStaticAccessor.getTimingsManager().getCurrentThreadTimings().start(); - } - - public static TimingsManager getTimingsManager() { - return TimingsStaticAccessor.getTimingsManager(); - } - - public int getId() { - return id; - } - - public long getEndTime() { - return endTime; - } - - public long getAcquireTime() { - return acquireTime; - } - - public boolean hasFinished() { - return endTime != -1; - } - - public void finish() { - if (hasFinished()) - throw new IllegalStateException("This timing was already finished"); - this.endTime = System.nanoTime(); - } - - @Override - public void close() { - finish(); - this.threadTimings.end(this); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/TimingsEventType.java b/event-loop/src/main/java/mc/core/timings/TimingsEventType.java deleted file mode 100644 index 181643a..0000000 --- a/event-loop/src/main/java/mc/core/timings/TimingsEventType.java +++ /dev/null @@ -1,17 +0,0 @@ -package mc.core.timings; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public enum TimingsEventType { - TIMINGS_START((short) 0), - TIMINGS_END((short) 1), - TIMINGS_FILE_INITIALIZING((short) 2), - TIMINGS_FILE_INITIALIZED((short) 3), - TIMINGS_CHANGE_THREAD_OPTIONS((short) 4), - TIMINGS_FILE_END((short) 5); - - @Getter - private final short id; -} \ No newline at end of file diff --git a/event-loop/src/main/java/mc/core/timings/TimingsManager.java b/event-loop/src/main/java/mc/core/timings/TimingsManager.java deleted file mode 100644 index 3e8ea66..0000000 --- a/event-loop/src/main/java/mc/core/timings/TimingsManager.java +++ /dev/null @@ -1,161 +0,0 @@ -package mc.core.timings; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import mc.core.timings.io.DefaultWriterFactory; -import mc.core.timings.io.TimingsWriter; -import mc.core.timings.io.TimingsWriterFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; - -@Slf4j -public class TimingsManager { - private final Map threadTimings = new ConcurrentHashMap<>(); - // These variables are essential in Timings thread synchronization - private final AtomicBoolean waitForFile = new AtomicBoolean(false); - private TimingsWriter writer; - private Thread timingsIoThread; - private CountDownLatch ioThreadStopMutex; - private BlockingQueue queue; - private ReentrantLock queueAccessLock = new ReentrantLock(); - // For modularity purposes - @Autowired - @Setter - private TimingsWriterFactory writerFactory = new DefaultWriterFactory(); - - public TimingsManager() { - TimingsStaticAccessor.TIMINGS_MANAGER = this; - } - - public void startRecording(File file) { - synchronized (waitForFile) { - waitForFile.set(true); - } - try { - writer = writerFactory.newInstance(file); - writer.writeEvent(0, 0, System.nanoTime(), TimingsEventType.TIMINGS_FILE_INITIALIZING); - // Synchronize current thread state - for (Map.Entry pair : threadTimings.entrySet()) { - writer.writeEvent(pair.getValue().getThreadId(), 0, System.nanoTime(), TimingsEventType.TIMINGS_CHANGE_THREAD_OPTIONS, "name: " + pair.getKey().getName()); - for (Timings timings : pair.getValue().getStack()) { - writer.writeEvent(pair.getValue().getThreadId(), timings.getId(), timings.getAcquireTime(), TimingsEventType.TIMINGS_START); - } - } - writer.writeEvent(0, 0, System.nanoTime(), TimingsEventType.TIMINGS_FILE_INITIALIZED); - queue = new ArrayBlockingQueue<>(200); - ioThreadStopMutex = new CountDownLatch(1); - timingsIoThread = new Thread() { - @Override - public void run() { - try { - while (!isInterrupted() && isAlive()) { - TimingsRecord record; - try { - if (queue == null) - return; - record = queue.take(); - } catch (InterruptedException e) { - return; - } - record.writeToFile(writer); - } - } finally { - ioThreadStopMutex.countDown(); - } - } - }; - timingsIoThread.setName("Timings IO thread"); - timingsIoThread.start(); - } catch (Exception e) { - log.error("Unable to start timings recording", e); - } - synchronized (waitForFile) { - waitForFile.set(false); - waitForFile.notifyAll(); - } - } - - public void stopRecording() { - // Disable write queue - queueAccessLock.lock(); - queue = null; - queueAccessLock.unlock(); - // Interrupt thread and wait until in finished writing the last task - timingsIoThread.interrupt(); - try { - ioThreadStopMutex.await(); - } catch (InterruptedException e) { - log.error("Unable to wait until last record would be written to file", e); - } - // Write EOF event - writer.writeEvent(0, 0, System.nanoTime(), TimingsEventType.TIMINGS_FILE_END); - // Unload file - try { - writer.close(); - } catch (IOException e) { - log.error("Unable to close timings file", e); - } - writer = null; - } - - void notifyTimings(ThreadTimings thread, Timings timings, boolean start) { - if (queue == null) - return; - queueAccessLock.lock(); - try { - if (queue != null) - queue.offer( - new TimingsRecord(thread.getThreadId(), - timings.getId(), - start ? timings.getAcquireTime() : timings.getEndTime(), - start ? TimingsEventType.TIMINGS_START : TimingsEventType.TIMINGS_END - ) - ); - } finally { - queueAccessLock.unlock(); - } - } - - void waitForTimingsInitialize() { - synchronized (waitForFile) { - while (waitForFile.get()) { - try { - waitForFile.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - } - - public ThreadTimings getCurrentThreadTimings() { - - synchronized (this.threadTimings) { - if (this.threadTimings.containsKey(Thread.currentThread())) { - return this.threadTimings.get(Thread.currentThread()); - } else { - ThreadTimings timings = new ThreadTimings(); - this.threadTimings.put(Thread.currentThread(), timings); - if (queue != null) { - try { - writer.writeEvent(timings.getThreadId(), 0, System.nanoTime(), TimingsEventType.TIMINGS_CHANGE_THREAD_OPTIONS, "name: " + Thread.currentThread().getName()); - } catch (NullPointerException ignored) { - // It means that there the file recording was stopped - // we don't actually care about it - } - } - return timings; - } - } - } -} diff --git a/event-loop/src/main/java/mc/core/timings/TimingsRecord.java b/event-loop/src/main/java/mc/core/timings/TimingsRecord.java deleted file mode 100644 index 3d3e3f6..0000000 --- a/event-loop/src/main/java/mc/core/timings/TimingsRecord.java +++ /dev/null @@ -1,33 +0,0 @@ -package mc.core.timings; - -import mc.core.timings.io.TimingsWriter; - -class TimingsRecord { - private int threadId; - private int stackId; - private long time; - private TimingsEventType eventType; - private String data; - - public TimingsRecord(int threadId, int stackId, long time, TimingsEventType eventType) { - this.threadId = threadId; - this.stackId = stackId; - this.time = time; - this.eventType = eventType; - } - - public TimingsRecord(int threadId, int stackId, long time, TimingsEventType eventType, String data) { - this.threadId = threadId; - this.stackId = stackId; - this.time = time; - this.eventType = eventType; - this.data = data; - } - - public void writeToFile(TimingsWriter fileWriter) { - if (data == null) - fileWriter.writeEvent(threadId, stackId, time, eventType); - else - fileWriter.writeEvent(threadId, stackId, time, eventType, data); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/TimingsStaticAccessor.java b/event-loop/src/main/java/mc/core/timings/TimingsStaticAccessor.java deleted file mode 100644 index 35d0ed4..0000000 --- a/event-loop/src/main/java/mc/core/timings/TimingsStaticAccessor.java +++ /dev/null @@ -1,9 +0,0 @@ -package mc.core.timings; - -public class TimingsStaticAccessor { - static TimingsManager TIMINGS_MANAGER; - - public static TimingsManager getTimingsManager() { - return TIMINGS_MANAGER != null ? TIMINGS_MANAGER : (TIMINGS_MANAGER = new TimingsManager()); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/io/DefaultWriterFactory.java b/event-loop/src/main/java/mc/core/timings/io/DefaultWriterFactory.java deleted file mode 100644 index 192ee03..0000000 --- a/event-loop/src/main/java/mc/core/timings/io/DefaultWriterFactory.java +++ /dev/null @@ -1,11 +0,0 @@ -package mc.core.timings.io; - -import java.io.File; -import java.io.IOException; - -public class DefaultWriterFactory implements TimingsWriterFactory { - @Override - public TimingsWriter newInstance(File file) throws IOException { - return new TimingsFileWriter(file); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/io/TimingsFileWriter.java b/event-loop/src/main/java/mc/core/timings/io/TimingsFileWriter.java deleted file mode 100644 index e2b593c..0000000 --- a/event-loop/src/main/java/mc/core/timings/io/TimingsFileWriter.java +++ /dev/null @@ -1,63 +0,0 @@ -package mc.core.timings.io; - - -import lombok.extern.slf4j.Slf4j; -import mc.core.timings.TimingsEventType; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.concurrent.locks.ReentrantLock; - -@SuppressWarnings("Duplicates") -@Slf4j -public class TimingsFileWriter implements TimingsWriter { - private FileOutputStream fileOutputStream; - private ObjectOutputStream writer; - private ReentrantLock lock = new ReentrantLock(); - - public TimingsFileWriter(File saveFile) throws IOException { - fileOutputStream = new FileOutputStream(saveFile); - writer = new ObjectOutputStream(fileOutputStream); - } - - @Override - public void writeEvent(int threadId, int stackId, long time, TimingsEventType type) { - lock.lock(); - try { - writer.writeInt(threadId); - writer.writeInt(stackId); - writer.writeLong(time); - writer.writeShort(type.getId()); - writer.writeBoolean(false); - } catch (IOException e) { - log.error("Unable to write timings record", e); - } finally { - lock.unlock(); - } - } - - @Override - public void writeEvent(int threadId, int stackId, long time, TimingsEventType type, String data) { - lock.lock(); - try { - writer.writeInt(threadId); - writer.writeInt(stackId); - writer.writeLong(time); - writer.writeShort(type.getId()); - writer.writeBoolean(true); - writer.writeUTF(data); - } catch (IOException e) { - log.error("Unable to write timings record", e); - } finally { - lock.unlock(); - } - } - - @Override - public void close() throws IOException { - writer.close(); - fileOutputStream.close(); - } -} diff --git a/event-loop/src/main/java/mc/core/timings/io/TimingsLogWriter.java b/event-loop/src/main/java/mc/core/timings/io/TimingsLogWriter.java deleted file mode 100644 index 0fa5fa3..0000000 --- a/event-loop/src/main/java/mc/core/timings/io/TimingsLogWriter.java +++ /dev/null @@ -1,24 +0,0 @@ -package mc.core.timings.io; - -import lombok.extern.slf4j.Slf4j; -import mc.core.timings.TimingsEventType; - -import java.io.IOException; - -@Slf4j -public class TimingsLogWriter implements TimingsWriter { - @Override - public void writeEvent(int threadId, int stackId, long time, TimingsEventType type) { - log.info("[{}] Thread #{}, Stack #{}: {}", time, threadId, stackId, type.toString()); - } - - @Override - public void writeEvent(int threadId, int stackId, long time, TimingsEventType type, String data) { - log.info("[{}] Thread #{}, Stack #{}: {} ({})", time, threadId, stackId, type.toString(), data); - } - - @Override - public void close() throws IOException { - - } -} diff --git a/event-loop/src/main/java/mc/core/timings/io/TimingsWriter.java b/event-loop/src/main/java/mc/core/timings/io/TimingsWriter.java deleted file mode 100644 index 640bdd5..0000000 --- a/event-loop/src/main/java/mc/core/timings/io/TimingsWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package mc.core.timings.io; - -import mc.core.timings.TimingsEventType; - -import java.io.IOException; - -public interface TimingsWriter { - void writeEvent(int threadId, int stackId, long time, TimingsEventType type); - - void writeEvent(int threadId, int stackId, long time, TimingsEventType type, String data); - - void close() throws IOException; -} diff --git a/event-loop/src/main/java/mc/core/timings/io/TimingsWriterFactory.java b/event-loop/src/main/java/mc/core/timings/io/TimingsWriterFactory.java deleted file mode 100644 index db12e6a..0000000 --- a/event-loop/src/main/java/mc/core/timings/io/TimingsWriterFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package mc.core.timings.io; - -import java.io.File; -import java.io.IOException; - -public interface TimingsWriterFactory { - TimingsWriter newInstance(File file) throws IOException; -} diff --git a/event-loop/src/test/java/mc/core/events/EventExecutorTest.java b/event-loop/src/test/java/mc/core/events/EventExecutorTest.java deleted file mode 100644 index a6c6c14..0000000 --- a/event-loop/src/test/java/mc/core/events/EventExecutorTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package mc.core.events; - -import mc.core.events.runner.ResourceAwareExecutorService; -import org.junit.Assert; -import org.junit.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -public class EventExecutorTest { - - @Test - public void basicTest() throws InterruptedException { - AtomicBoolean testVariable = new AtomicBoolean(false); - CountDownLatch latch = new CountDownLatch(1); - ResourceAwareExecutorService service = new ResourceAwareExecutorService(1); - service.start(); - service.addTask(() -> { - testVariable.set(true); - latch.countDown(); - }); - - latch.await(1, TimeUnit.SECONDS); - service.stop(); - Assert.assertTrue("Scheduled task was not executed", testVariable.get()); - - } -} diff --git a/event-loop/src/test/java/mc/core/events/EventLoopTest.java b/event-loop/src/test/java/mc/core/events/EventLoopTest.java deleted file mode 100644 index a92e738..0000000 --- a/event-loop/src/test/java/mc/core/events/EventLoopTest.java +++ /dev/null @@ -1,169 +0,0 @@ -package mc.core.events; - -import mc.core.events.api.EventHandler; -import mc.core.events.api.EventPriority; -import mc.core.events.api.EventQueueOwner; -import mc.core.events.api.Plugin; -import mc.core.events.runner.ResourceAwareExecutorService; -import org.junit.Assert; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -@SuppressWarnings("Duplicates") -public class EventLoopTest { - - @Test - public void basicTest() throws InterruptedException { - Plugin plugin = new Plugin() { - }; - - EventQueueOwner queueOwner = new EventQueueOwner() { - }; - - - CountDownLatch latch = new CountDownLatch(1); - FullAsyncEventLoop eventLoop = new FullAsyncEventLoop(); - eventLoop.addEventHandler(plugin, new Object() { - @EventHandler - public void onLoginEvent(LoginEvent event) { - - latch.countDown(); - } - }); - - ResourceAwareExecutorService service = new ResourceAwareExecutorService(1); - service.start(); - - eventLoop.setResourceAwareExecutorService(service); - eventLoop.asyncFireEvent(queueOwner, new LoginEvent(null)); - - latch.await(1, TimeUnit.SECONDS); - Assert.assertEquals("Event was not called", 0, latch.getCount()); - } - - @Test - public void consecutiveExecutionTest() throws InterruptedException { - Plugin plugin = new Plugin() { - }; - - EventQueueOwner queueOwner = new EventQueueOwner() { - }; - - - CountDownLatch latch = new CountDownLatch(2); - FullAsyncEventLoop eventLoop = new FullAsyncEventLoop(); - eventLoop.addEventHandler(plugin, new Object() { - @EventHandler - public void onLoginEvent(LoginEvent event) { - - latch.countDown(); - } - }); - - ResourceAwareExecutorService service = new ResourceAwareExecutorService(1); - service.start(); - - eventLoop.setResourceAwareExecutorService(service); - - eventLoop.asyncFireEvent(queueOwner, new LoginEvent(null)); - eventLoop.asyncFireEvent(queueOwner, new LoginEvent(null)); - - latch.await(1, TimeUnit.SECONDS); - Assert.assertEquals("Event was not called", 0, latch.getCount()); - } - - @Test - public void prioritySystemTest() throws InterruptedException { - Plugin plugin = new Plugin() { - }; - - EventQueueOwner queueOwner = new EventQueueOwner() { - }; - - - CountDownLatch latch = new CountDownLatch(3); - FullAsyncEventLoop eventLoop = new FullAsyncEventLoop(); - List priorities = new ArrayList<>(3); - - eventLoop.addEventHandler(plugin, new Object() { - @EventHandler(priority = EventPriority.NORMAL) - public void login1(LoginEvent event) { - priorities.add(0); - latch.countDown(); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void login2(LoginEvent event) { - priorities.add(1); - latch.countDown(); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void login3(LoginEvent event) { - priorities.add(2); - latch.countDown(); - } - }); - - ResourceAwareExecutorService service = new ResourceAwareExecutorService(1); - service.start(); - - eventLoop.setResourceAwareExecutorService(service); - - eventLoop.asyncFireEvent(queueOwner, new LoginEvent(null)); - - latch.await(1, TimeUnit.SECONDS); - Assert.assertEquals("Incorrect call sequence", "[2, 0, 1]", priorities.toString()); - } - - @Test - public void ignoreCancelledTest() throws InterruptedException { - Plugin plugin = new Plugin() { - }; - - EventQueueOwner queueOwner = new EventQueueOwner() { - }; - - - CountDownLatch latch = new CountDownLatch(1); - FullAsyncEventLoop eventLoop = new FullAsyncEventLoop(); - List priorities = new ArrayList<>(2); - - eventLoop.addEventHandler(plugin, new Object() { - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void login1(LoginEvent event) { - priorities.add(0); - event.setCanceled(true); - } - - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void login2(LoginEvent event) { - priorities.add(1); - } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void login3(LoginEvent event) { - priorities.add(2); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void monitor(LoginEvent event) { - latch.countDown(); - } - }); - - ResourceAwareExecutorService service = new ResourceAwareExecutorService(1); - service.start(); - - eventLoop.setResourceAwareExecutorService(service); - - eventLoop.asyncFireEvent(queueOwner, new LoginEvent(null)); - - latch.await(1, TimeUnit.SECONDS); - Assert.assertEquals("Incorrect call sequence", "[2, 0]", priorities.toString()); - } -} diff --git a/event-loop/src/test/java/mc/core/events/LockTest.java b/event-loop/src/test/java/mc/core/events/LockTest.java deleted file mode 100644 index e1e65a2..0000000 --- a/event-loop/src/test/java/mc/core/events/LockTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package mc.core.events; - -import mc.core.events.runner.lock.LockObserveList; -import mc.core.events.runner.lock.PoorMansLock; -import org.junit.Assert; -import org.junit.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -public class LockTest { - @Test - public void basicTest() throws InterruptedException { - AtomicBoolean engageCallbackCalled = new AtomicBoolean(false); - AtomicBoolean disengageCallbackCalled = new AtomicBoolean(false); - - PoorMansLock lock = new PoorMansLock(); - lock.addCallback(lock1 -> { - if (lock1.isLocked()) - engageCallbackCalled.set(true); - else - disengageCallbackCalled.set(true); - }); - lock.lock(); - Assert.assertTrue("Lock is not locked", lock.isLocked()); - Assert.assertTrue("Engage callback was not called", engageCallbackCalled.get()); - - engageCallbackCalled.set(false); - try { - lock.lock(); - Assert.assertFalse("Engage callback was called from attempt to block from the same thread", engageCallbackCalled.get()); - } catch (Exception ex) { - Assert.fail("Exception fired while attempting to lock from the same thread"); - return; - } - - Assert.assertFalse("Disengage callback was called while not actually disengaging [x1]", disengageCallbackCalled.get()); - - AtomicBoolean lockExceptionFired = new AtomicBoolean(false); - AtomicBoolean unlockExceptionFired = new AtomicBoolean(false); - CountDownLatch latch = new CountDownLatch(1); - new Thread(() -> { - try { - lock.lock(); - } catch (Exception ex) { - lockExceptionFired.set(true); - } - try { - lock.unlock(); - } catch (Exception ex) { - unlockExceptionFired.set(true); - } - latch.countDown(); - }).start(); - - latch.await(); - Assert.assertTrue("Exception was not fired on concurrent lock attempt", lockExceptionFired.get()); - Assert.assertTrue("Exception was not fired on non-owner unlock attempt", unlockExceptionFired.get()); - Assert.assertFalse("Disengage callback was called while not actually disengaging [x2]", disengageCallbackCalled.get()); - - lock.unlock(); - Assert.assertTrue("Disengage callback was on called on lock disengage", disengageCallbackCalled.get()); - } - - @Test - public void observeListTest() { - PoorMansLock lock1 = new PoorMansLock(); - PoorMansLock lock2 = new PoorMansLock(); - - LockObserveList list = new LockObserveList(); - list.add(lock1); - list.add(lock2); - - Assert.assertTrue("LockObserveList was no able to correctly identify lock states for unlocked locks", list.isReady()); - lock1.lock(); - Assert.assertFalse("LockObserveList was no able to correctly identify lock states for list with one locked lock", list.isReady()); - - - AtomicBoolean listReadyCallbackCalled = new AtomicBoolean(false); - list.setCallback(() -> listReadyCallbackCalled.set(true)); - lock2.lock(); - - Assert.assertFalse("Callback was called when another lock got engaged", listReadyCallbackCalled.get()); - lock1.unlock(); - Assert.assertFalse("Callback was called while one lock is still locked", listReadyCallbackCalled.get()); - lock2.unlock(); - Assert.assertTrue("Callback was not called when both locks are actually free", listReadyCallbackCalled.get()); - - } -} diff --git a/event-loop/src/test/java/mc/core/timings/TimingsTest.java b/event-loop/src/test/java/mc/core/timings/TimingsTest.java deleted file mode 100644 index a3e6200..0000000 --- a/event-loop/src/test/java/mc/core/timings/TimingsTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package mc.core.timings; - -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -public class TimingsTest { - @Test - public void basicTest() { - try (Timings timings = Timings.start()) { - System.out.println("Test code"); - } - } - - @Test - public void brokenTimingTest() { - try (Timings timings = Timings.start()) { - Timings t1 = Timings.start(); - Timings.start(); - System.out.println("Pre Close t1"); - t1.close(); - System.out.println("Finished"); - } - } - - @Test - public void fileRecording() throws IOException { - Timings.getTimingsManager().startRecording(new File("test.timings")); - - try (Timings t1 = Timings.start()) { - try { - Thread.sleep(20); - } catch (InterruptedException e) { - e.printStackTrace(); - } - try (Timings t2 = Timings.start()) { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - Thread.sleep(5); - - } catch (InterruptedException e) { - e.printStackTrace(); - } - - - Timings.getTimingsManager().stopRecording(); - } -} diff --git a/generated_world/README.MD b/generated_world/README.MD deleted file mode 100644 index f49eaf7..0000000 --- a/generated_world/README.MD +++ /dev/null @@ -1,3 +0,0 @@ -### System properties: - -* `worlds.folder` -- folder where worlds will be located \ No newline at end of file diff --git a/generated_world/build.gradle b/generated_world/build.gradle deleted file mode 100644 index 5c7cdc7..0000000 --- a/generated_world/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -group 'mc' -version '1.0-SNAPSHOT' - -dependencies { - compile_excludeCopy project(':core') - testCompile group: 'junit', name: 'junit', version: '4.12' -} diff --git a/generated_world/src/main/java/mc/world/generated_world/WorldConstants.java b/generated_world/src/main/java/mc/world/generated_world/WorldConstants.java deleted file mode 100644 index cd33fde..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/WorldConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -package mc.world.generated_world; - -public final class WorldConstants { - - public static final boolean DEBUG_ENABLED = true; - - public static final String CHUNK_FILE_NAME_TEMPLATE = "chunk_{0}_{1}_{2}.dat"; - public static final String BIOME_FILE_NAME_TEMPLATE = "biomes.dat"; - public static final String WORLD_INFO_FILE_NAME_TEMPLATE = "world.dat"; - public static final String REGION_FILE_NAME_TEMPLATE = "r.{0}.{1}"; - - public static final int WORLD_MAX_HEIGHT = 256; - public static final int WORLD_SEA_LEVEL = 64; - public static final int WORLD_MIN_GENERATION_HEIGHT = 36; - public static final int WORLD_MAX_GENERATION_HEIGHT = 128; - public static final int WORLD_REGION_SIZE = 256; - public static final int WORLD_CHUNK_SIZE = 16; - public static final int WORLD_MAX_TEMPERATURE = 100; - public static final int WORLD_MAX_WETNESS = 100; - public static final int WORLD_BASE_WETNESS = 80; - - public static final double WORLD_LAND_SIZE = 63.03; - public static final double WORLD_LAKE_SIZE = 9.3; - public static final double WORLD_WET_SEA_PERCENT = 0.8; - public static final double WORLD_TEMPERATURE_SIZE = 41.0; - public static final double WORLD_TEMPERATURE_ZONE_SIZE = 2.99; - public static final double WORLD_TEMPERATURE_HEIGHT_GRAD_SIZE = 1.1; - - public static final int LANDFILL_GRASS_SURFACE_THIN = 5; - public static final double WORLD_ROUGHNESS = 0.35; - - private WorldConstants () {} -} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java deleted file mode 100644 index 27baadc..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -package mc.world.generated_world.chunk; - -import lombok.Getter; -import mc.core.exception.ResourceUnloadedException; -import mc.core.world.chunk.ChunkSection; -import mc.core.world.Region; -import mc.core.world.World; -import mc.core.world.chunk.Chunk; - -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; - -import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE; - -public class ChunkImpl implements Chunk { - @Getter - private final int x; - @Getter - private final int z; - private Reference regionReference; - private ChunkSection[] sections = new ChunkSection[WORLD_CHUNK_SIZE]; - - public ChunkImpl (int x, int z, Region region) { - this.x = x; - this.z = z; - this.regionReference = new WeakReference<>(region); - } - - @Override - public World getWorld() { - Region region = getRegion(); - if (region == null) { - throw new ResourceUnloadedException("Region is unloaded"); - } - return region.getWorld(); - } - - @Override - public ChunkSection getChunkSection(int height) { - return sections[height]; - } - - @Override - public ChunkSection setChunkSection(int height, ChunkSection chunkSection) { - sections[height] = chunkSection; - return chunkSection; - } - - @Override - public Region getRegion() { - if (regionReference == null) { - return null; - } - - if (regionReference.get() == null) { - throw new ResourceUnloadedException("Region is unloaded"); - } - - return regionReference.get(); - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java deleted file mode 100644 index ab10c71..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -package mc.world.generated_world.chunk; - -import lombok.Getter; -import mc.core.exception.ResourceUnloadedException; -import mc.core.world.Biome; -import mc.core.world.chunk.ChunkSection; -import mc.core.world.Region; -import mc.core.world.World; -import mc.core.world.block.Block; -import mc.core.world.block.BlockFactory; -import mc.core.world.block.BlockType; - -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; - -import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE; - -public class ChunkSectionImpl implements ChunkSection { - @Getter - private final int x; - @Getter - private final int y; - @Getter - private final int z; - private final Block[][][] blocks = new Block[WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE][WORLD_CHUNK_SIZE]; - private final transient Reference region; - private BlockFactory blockFactory = new BlockFactory(); - - public ChunkSectionImpl(int x, int y, int z, Region region) { - this.x = x; - this.y = y; - this.z = z; - this.region = new WeakReference<>(region); - } - - @Override - public int getSkyLight(int x, int y, int z) { - return 15; - } - - @Override - public void setSkyLight(int x, int y, int z, int lightLevel) { - - } - - @Override - public int getAddition(int x, int y, int z) { - return 0; - } - - @Override - public void setAddition(int x, int y, int z, int value) { - - } - - @Override - public Biome getBiome(int x, int z) { - return getRegion().getBiomeAt(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE); - } - - @Override - public void setBiome(int x, int z, Biome biome) { - getRegion().setBiome(x + this.x * WORLD_CHUNK_SIZE,z + this.z * WORLD_CHUNK_SIZE, biome); - } - - @Override - public void setBlock(Block block) { - if (block.getBlockType() == BlockType.AIR) { - blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = null; - return; - } - blocks[block.getLocation().getBlockX()][block.getLocation().getBlockY()][block.getLocation().getBlockZ()] = block; - } - - @Override - public Block getBlock(int x, int y, int z) { - Block block = blocks[x][y][z]; - if (block == null) { - return blockFactory.create(BlockType.AIR, 0, x, y, z); - } - return blocks[x][y][z]; - } - - @Override - public Region getRegion() { - if (region == null) { - return null; - } - if (region.get() == null) { - throw new ResourceUnloadedException("Region is unloaded"); - } - return region.get(); - } - - @Override - public World getWorld() { - return getRegion().getWorld(); - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java b/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java deleted file mode 100644 index 0aa6513..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/ChunkSectionProxy.java +++ /dev/null @@ -1,108 +0,0 @@ -package mc.world.generated_world.chunk; - -import mc.core.world.chunk.ChunkSection; -import mc.core.world.Region; -import mc.core.world.World; -import mc.core.world.block.Block; -import mc.core.world.Biome; - -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -public class ChunkSectionProxy implements ChunkSection { - private final ChunkSection chunk; - private volatile transient long lastUsage = System.currentTimeMillis(); - private final transient ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - - public ChunkSectionProxy(ChunkSection chunk) { - this.chunk = chunk; - } - - public long getLastUsage() { - synchronized (chunk) { - return lastUsage; - } - } - - private final void use () { - synchronized (chunk) { - lastUsage = System.currentTimeMillis(); - } - } - - @Override - public Block getBlock(int x, int y, int z) { - use(); - return chunk.getBlock(x, y, z); - } - - @Override - public Region getRegion() { - return chunk.getRegion(); - } - - @Override - public World getWorld() { - return chunk.getWorld(); - } - - @Override - public void setBlock(Block block) { - use(); - chunk.setBlock(block); - } - - @Override - public int getSkyLight(int x, int y, int z) { - use(); - return chunk.getSkyLight(x, y, z); - } - - @Override - public void setSkyLight(int x, int y, int z, int lightLevel) { - use(); - chunk.setSkyLight(x, y, z, lightLevel); - } - - @Override - public int getAddition(int x, int y, int z) { - use(); - return chunk.getAddition(x, y, z); - } - - @Override - public void setAddition(int x, int y, int z, int value) { - use(); - chunk.setAddition(x, y, z, value); - } - - @Override - public Biome getBiome(int x, int z) { - use(); - return chunk.getBiome(x, z); - } - - @Override - public void setBiome(int x, int z, Biome biome) { - use(); - chunk.setBiome(x, z, biome); - } - - @Override - public int getX() { - use(); - return chunk.getX(); - } - - @Override - public int getY() { - use(); - return chunk.getY(); - } - - @Override - public int getZ() { - use(); - return chunk.getZ(); - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java b/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java deleted file mode 100644 index ac6d323..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/chunk/InMemoryCacheChunkLoader.java +++ /dev/null @@ -1,110 +0,0 @@ -package mc.world.generated_world.chunk; - -import lombok.extern.slf4j.Slf4j; -import mc.core.serialization.Serializer; -import mc.core.world.*; -import mc.core.world.chunk.ChunkLoader; -import mc.core.world.chunk.ChunkSection; -import mc.world.generated_world.serialization.ChunkReader; -import mc.world.generated_world.serialization.RegionReaderWriter; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.Optional; - -import static mc.world.generated_world.WorldConstants.*; - -@Slf4j -public class InMemoryCacheChunkLoader implements ChunkLoader { - - private final World world; - private File worldFolder; - @Autowired - private WorldGenerator worldGenerator; - @Autowired - private ChunkReader chunkReader; - @Autowired - private Serializer chunkSerializer; - @Autowired - private RegionReaderWriter regionReaderWritter; - - public InMemoryCacheChunkLoader(World world) { - this.world = world; - String worldPath = System.getProperty("worlds.folder", "worlds"); - worldFolder = new File(worldPath, world.getWorldId().toString()); - if (!worldFolder.exists()) { - log.info("Created folder for world with uuid '{}'", world.getWorldId()); - worldFolder.mkdirs(); - } - } - - private File getChuckFile(int x, int y, int z) { - return new File(worldFolder, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z)); - } - - @Override - public Optional loadChunk(int x, int y, int z) { - File file = getChuckFile(x, y, z); - if (!file.exists()) { - return Optional.empty(); - } else { - try { - ChunkSection chunkSection = chunkReader.read(world.getRegion(x / WORLD_CHUNK_SIZE, z / WORLD_CHUNK_SIZE), x, y, z); - return Optional.of(chunkSection); - } catch (IOException e) { - log.error("Error occurred while reading chunk file: " + file.getAbsolutePath(), e); - return Optional.empty(); - } - } - } - - @Override - public ChunkSection loadOrGenerateChunk(int x, int y, int z) { - int regX = x / WORLD_CHUNK_SIZE; - int regZ = z / WORLD_CHUNK_SIZE; - File regionFile = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, regX, regZ)); - Region region; - ChunkSection chunkSection; - if (!regionFile.exists()) { - log.debug("Region [{}, {}] not found. Generating!", regX, regZ); - regionFile.mkdirs(); - region = worldGenerator.generateRegion(regX, regZ, world); - try { - regionReaderWritter.write(region); - } catch (IOException e) { - log.error("Error occurred while writting biome file", e); - } - saveRegion(region); - chunkSection = region.getChunkAt(x % WORLD_CHUNK_SIZE, y % WORLD_CHUNK_SIZE, z % WORLD_CHUNK_SIZE); - } else { - try { - region = regionReaderWritter.read(regX, regZ, world); - chunkSection = chunkReader.read(region, x, y, z); - } catch (IOException e) { - log.error("Error occurred while reading chunkSection file", e); - return null; - } - } - return chunkSection; - } - - private void saveRegion (Region region) { - File file = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ())); - for (int x = 0; x < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; x ++) { - for (int y = 0; y < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; y ++) { - for (int z = 0; z < WORLD_REGION_SIZE / WORLD_CHUNK_SIZE; z ++) { - byte[] chunkBytes = chunkSerializer.serialize(region.getChunkAt(x, y, z)); - File chunkFile = new File(file, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z)); - try (FileOutputStream writer = new FileOutputStream(chunkFile)) { - writer.write(chunkBytes); - } catch (IOException e) { - log.error("Error occurred while writting chunk to file", e); - } - } - } - } - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java b/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java deleted file mode 100644 index b3c84c7..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/generator/NoiseGenerator.java +++ /dev/null @@ -1,57 +0,0 @@ -package mc.world.generated_world.generator; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import static mc.world.generated_world.WorldConstants.WORLD_REGION_SIZE; - -@Slf4j -@RequiredArgsConstructor -public class NoiseGenerator { - private int[] perm = new int[WORLD_REGION_SIZE]; - private double[] gradsX = new double[WORLD_REGION_SIZE]; - private double[] gradsY = new double[WORLD_REGION_SIZE]; - private final int seed; - - void init() { - for (int i = 0; i < WORLD_REGION_SIZE; ++i) { - int other = rand(i) % (i + 1); - if (i > other) - perm[i] = perm[other]; - perm[other] = i; - gradsX[i] = Math.cos(2.0f * Math.PI * i / WORLD_REGION_SIZE); - gradsY[i] = Math.sin(2.0f * Math.PI * i / WORLD_REGION_SIZE); - } - log.debug("Noise generator is initialized"); - } - - double f(double t) { - t = Math.abs(t); - return t >= 1.0f ? 0.0f : 1.0f - - (3.0f - 2.0f * t) * t * t; - } - - private double surflet(double x, double y, double gradX, double gradY) { - return f(x) * f(y) * (gradX * x + gradY * y); - } - - double noise(double x, double y) { - float result = 0.0f; - int cellX = (int)(x); - int cellY = (int)(y); - int mask = WORLD_REGION_SIZE - 1; - for (int gridY = cellY; gridY <= cellY + 1; ++gridY) - for (int gridX = cellX; gridX <= cellX + 1; ++gridX) { - int hash = perm[(perm[gridX & mask] + gridY) & mask]; - result += surflet(x - gridX, y - gridY, - gradsX[hash], gradsY[hash]); - } - return (result + 1) / 2; - } - - private int rand(int i) { - int x = (i * i) % WORLD_REGION_SIZE; - int y = (i + i * x) % WORLD_REGION_SIZE; - return (int) (Integer.MAX_VALUE * SeedRandomGenerator.random(x, y, seed)); - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java b/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java deleted file mode 100644 index 5da096f..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/generator/SeedBasedWorldGenerator.java +++ /dev/null @@ -1,392 +0,0 @@ -package mc.world.generated_world.generator; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import mc.core.world.block.BlockFactory; -import mc.core.world.block.BlockType; -import mc.core.world.*; -import mc.core.world.chunk.ChunkSection; -import mc.world.generated_world.region.RegionImpl; -import mc.world.generated_world.world.CubicWorld; -import mc.world.generated_world.world.Temperature; -import mc.world.generated_world.world.Wetness; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.UUID; - -import static mc.world.generated_world.WorldConstants.*; - -@Slf4j -public class SeedBasedWorldGenerator implements WorldGenerator { - - public static void main(String[] args) throws Exception{ - WorldGenerator worldGenerator = new SeedBasedWorldGenerator(); - World world = new CubicWorld(UUID.fromString("00000000-0000-0000-C000-000000000046"), 2626949); - /*Region region = worldGenerator.generateRegion(0, 0, world); - region.save(new ChunkSerializer(), new RegionReaderWriter(new File("worlds", world.getWorldId().toString()))); - new WorldReaderWriter(new File("worlds")).writeWorldInfo(world);*/ - - createBigImage(worldGenerator, world); - } - - private static void createBigImage (WorldGenerator worldGenerator, World world) throws IOException { - BufferedImage image = new BufferedImage(3 * 256, 3 * 256, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x <= 2; x ++) { - for (int z = 0; z <= 2; z ++) { - worldGenerator.generateRegion(x - 1, z - 1, world); - addToBigImage(x, z, image); - } - } - ImageIO.write(image, "png", new File("out", "merged.png")); - } - - private static void addToBigImage (int shiftX, int shiftY, BufferedImage image) throws IOException{ - BufferedImage currentImage = ImageIO.read(new File("out/" + (shiftX - 1) + "." + (shiftY - 1), "biomeMap.png")); - for (int x = 0; x < 256; x ++){ - for (int y = 0; y < 256; y ++){ - int tx = 256 * shiftX + x; - int ty = 256 * shiftY + y; - image.setRGB(tx, ty, currentImage.getRGB(x, y)); - } - } - } - - @Override - public Region generateRegion(int x, int z, World world) { - log.info("Generating region [{},{}]...", x, z); - Region region = new RegionImpl(x, z, world); - RegionGenerator regionGenerator = new RegionGenerator(world, region); - regionGenerator.generate(); - log.info("Region [{},{}] is generated", x, z); - return region; - } - - @RequiredArgsConstructor - private class RegionGenerator { - private final World world; - private final Region region; - private NoiseGenerator noiseGenerator; - private BlockFactory blockFactory = new BlockFactory(); - - private double sigmoid (double x) { - x -= 0.5; - x *= 15; - return 1.0 / (1.0 + Math.exp(-x)); - } - - private int convert (int x) { - return 40960 + x; - } - - private void generate() { - log.debug("Starting generating region [{}, {}] for world '{}' with seed '{}'", region.getX(), region.getZ(), world.getWorldId(), world.getSeed()); - - noiseGenerator = new NoiseGenerator(world.getSeed()); - noiseGenerator.init(); - - int[][] heightMap = new int[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - int[][] grassMap = new int[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - int[][] temperatureMap = new int[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - int[][] wetMap = new int[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - Biome[][] biomes = new Biome[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - - for (int x = 0; x < WORLD_REGION_SIZE; x ++) { - for (int z = 0; z < WORLD_REGION_SIZE; z ++) { - int tx = convert(x + region.getX() * WORLD_REGION_SIZE); - int tz = convert(z + region.getZ() * WORLD_REGION_SIZE); - double p = sigmoid(noiseGenerator.noise(tx / WORLD_LAND_SIZE, tz / WORLD_LAND_SIZE)); - double r = Math.sqrt(noiseGenerator.noise(tx / WORLD_LAKE_SIZE, tz / WORLD_LAKE_SIZE)); - double h = (WORLD_MAX_GENERATION_HEIGHT - WORLD_MIN_GENERATION_HEIGHT) * Math.min(p * r, 1); - h = Math.min(WORLD_MAX_GENERATION_HEIGHT, h + WORLD_MIN_GENERATION_HEIGHT); - heightMap[x][z] = (int)(h); - grassMap[x][z] = (int) (1 + SeedRandomGenerator.random(tx, tz, world.getSeed()) * (LANDFILL_GRASS_SURFACE_THIN - 1)); - double k = Math.sqrt(noiseGenerator.noise(tx * WORLD_TEMPERATURE_ZONE_SIZE, tz * WORLD_TEMPERATURE_ZONE_SIZE)); - double q = Math.sqrt(noiseGenerator.noise(tx / WORLD_TEMPERATURE_SIZE, tz / WORLD_TEMPERATURE_SIZE)); - temperatureMap[x][z] = (int) (WORLD_MAX_TEMPERATURE * Math.min((k * k + q * q + k * q) * k * q, 0.99)); - if (heightMap[x][z] < WORLD_SEA_LEVEL) { - biomes[x][z] = Biome.OCEAN; - wetMap[x][z] = (int) (WORLD_MAX_WETNESS * WORLD_WET_SEA_PERCENT *noiseGenerator.noise(tx, tz)); - } else { - int th = heightMap[x][z] - WORLD_SEA_LEVEL; - th = (int) (th * (1 + 1.25 * th / (WORLD_MAX_GENERATION_HEIGHT - WORLD_SEA_LEVEL))); - heightMap[x][z] = Math.min(WORLD_SEA_LEVEL + th, WORLD_MAX_GENERATION_HEIGHT); - } - } - } - - for (int x = 1; x < WORLD_REGION_SIZE - 1; x ++) { - for (int z = 1; z < WORLD_REGION_SIZE - 1; z++) { - int mid = 0; - for (int tx = x - 1; tx <= x + 1; tx ++) { - for (int tz = z - 1; tz <= z + 1; tz ++) { - mid += wetMap[tx][tz]; - } - } - wetMap[x][z] = mid / 9; - } - } - for (int z = 1; z < WORLD_REGION_SIZE - 1; z++) { - for (int x = 1; x < WORLD_REGION_SIZE - 1; x ++) { - int mid = 0; - for (int tx = x - 1; tx <= x + 1; tx ++) { - for (int tz = z - 1; tz <= z + 1; tz ++) { - mid += wetMap[tx][tz]; - } - } - wetMap[x][z] = (int) (mid / 9 * (1 + 0.4 * SeedRandomGenerator.random(x, z, world.getSeed()))); - temperatureMap[x][z] = (int) Math.min(Math.max(temperatureMap[x][z] - WORLD_TEMPERATURE_HEIGHT_GRAD_SIZE * SeedRandomGenerator.random(x, z, world.getSeed()) * (heightMap[x][z] - WORLD_SEA_LEVEL), 0), WORLD_MAX_TEMPERATURE); - } - } - - for (int z = 1; z < WORLD_REGION_SIZE - 1; z++) { - for (int x = 1; x < WORLD_REGION_SIZE - 1; x ++) { - wetMap[x][z] = (int) Math.min(WORLD_MAX_WETNESS, WORLD_BASE_WETNESS * noiseGenerator.noise(x / 31d, z / 31d) + wetMap[x][z] * (1 + 0.2 * (SeedRandomGenerator.random(x, z, world.getSeed())))); - } - } - - smooth(grassMap); - smooth(temperatureMap); - smooth(wetMap); - //smooth(heightMap); - - // ================================ DEBUG ======================================= - if (DEBUG_ENABLED) { - log.debug("Creating debug images"); - File outFile; - outFile = new File("out", region.getX() + "." + region.getZ()); - outFile.mkdirs(); - BufferedImage tempImg = new BufferedImage(WORLD_REGION_SIZE, WORLD_REGION_SIZE, BufferedImage.TYPE_INT_RGB); - BufferedImage wetImg = new BufferedImage(WORLD_REGION_SIZE, WORLD_REGION_SIZE, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < WORLD_REGION_SIZE; x ++) { - for (int z = 0; z < WORLD_REGION_SIZE; z ++) { - Temperature temperature = Temperature.values()[Temperature.values().length * temperatureMap[x][z] / WORLD_MAX_TEMPERATURE]; - Wetness wetness = Wetness.values()[ Wetness.values().length * (Math.min(wetMap[x][z], WORLD_MAX_WETNESS) - 1) / WORLD_MAX_WETNESS]; - biomes[x][z] = selectBiome(temperature, wetness, heightMap[x][z]); - tempImg.setRGB(x, z, temperature.ordinal() * 0xff / Temperature.values().length); - wetImg.setRGB(x, z, wetness.ordinal() * 0xff / Wetness.values().length); - } - } - - try { - ImageIO.write(tempImg, "png", new File(outFile, "temp_img.png")); - ImageIO.write(wetImg, "png", new File(outFile, "wet_img.png")); - - BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - BufferedImage subImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < 256; x++) { - for (int z = 0; z < 256; z++) { - int h = heightMap[x][z]; - h = h << 16 | h << 8 | h; - image.setRGB(x, z, h); - } - } - ImageIO.write(image, "png", new File(outFile, "heightmap.png")); - image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < 256; x++) { - for (int z = 0; z < 256; z++) { - int temp = 0xff * temperatureMap[x][z] / 100; - temp = temp << 16; - image.setRGB(x, z, temp); - subImage.setRGB(x, z, (0xff * (int) (temperatureMap[x][z] / 20) / 5) << 16); - } - } - ImageIO.write(image, "png", new File(outFile, "temperatureMap.png")); - ImageIO.write(subImage, "png", new File(outFile, "reg_temperatureMap.png")); - subImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < 256; x++) { - for (int z = 0; z < 256; z++) { - int wet = 0xff * wetMap[x][z] / 100; - image.setRGB(x, z, wet); - subImage.setRGB(x, z, 0xff * (int) (Wetness.values().length * wetMap[x][z] / (WORLD_MAX_WETNESS))); - } - } - ImageIO.write(image, "png", new File(outFile, "wetMap.png")); - ImageIO.write(subImage, "png", new File(outFile, "reg_wetMap.png")); - image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB); - for (int x = 0; x < 256; x++) { - for (int z = 0; z < 256; z++) { - image.setRGB(x, z, biomes[x][z].getColor()); - } - } - ImageIO.write(image, "png", new File(outFile, "biomeMap.png")); - } catch (Exception e) { - log.error("Error occurred while creating debug images", e); - } - } - // ================================ DEBUG FINISH ======================================= - - log.debug("Creating chunks..."); - - for (int x = 0; x < WORLD_REGION_SIZE; x ++) { - for (int z = 0; z < WORLD_REGION_SIZE; z ++) { - region.setBiome(x, z, biomes[x][z]); - if (heightMap[x][z] < WORLD_SEA_LEVEL) { - for (int y = 0; y < WORLD_SEA_LEVEL; y ++) { - ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16); - if (y == 0) { - chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16)); - continue; - } - if (y < heightMap[x][z]) { - if (y < heightMap[x][z] - grassMap[x][z]) { - chunk.setBlock(blockFactory.create(BlockType.STONE, 0, x % 16, y % 16, z % 16)); - } else { - chunk.setBlock(blockFactory.create(BlockType.SAND, 0, x % 16, y % 16, z % 16)); - } - } else { - chunk.setBlock(blockFactory.create(BlockType.WATER, 0, x % 16, y % 16, z % 16)); - } - } - } else { - for (int y = 0; y < heightMap[x][z]; y++) { - ChunkSection chunk = region.getChunkAt(x / 16, y / 16, z / 16); - if (y == 0) { - chunk.setBlock(blockFactory.create(BlockType.BEDROCK, 0, x % 16, y % 16, z % 16)); - continue; - } - if (y < heightMap[x][z] - grassMap[x][z]) { - chunk.setBlock(blockFactory.create(BlockType.STONE, 0, x % 16, y % 16, z % 16)); - } else { - if (biomes[x][z] == Biome.DESERT || biomes[x][z] == Biome.DESERT_HILLS) { - chunk.setBlock(blockFactory.create(BlockType.SAND, 0, x % 16, y % 16, z % 16)); - } else if (biomes[x][z] == Biome.TAIGA || biomes[x][z] == Biome.TAIGA_HILLS) { - chunk.setBlock(blockFactory.create(BlockType.DIRT, 0, x % 16, y % 16, z % 16)); - } else { - chunk.setBlock(blockFactory.create(BlockType.GRASS, 0, x % 16, y % 16, z % 16)); - } - } - } - } - } - } - - /* TODO - log.debug("Creating rivers..."); - log.debug("Creating caves..."); - log.debug("Generating ores..."); - log.debug("Creating structures..."); - log.debug("Planting trees..."); - log.debug("Spawning animals..."); - */ - } - - private Biome selectBiome (Temperature temperature, Wetness wetness, int height) { - - if (wetness == Wetness.WATER || height < WORLD_SEA_LEVEL) { - if (temperature == Temperature.FROST) { - if (height < WORLD_SEA_LEVEL) { - return Biome.FROZEN_OCEAN; - } else { - return Biome.ICE_PLAINS; - } - } else { - if (height < WORLD_SEA_LEVEL) { - if (height < WORLD_MIN_GENERATION_HEIGHT + (WORLD_SEA_LEVEL - WORLD_MIN_GENERATION_HEIGHT) / 2) { - return Biome.DEEP_OCEAN; - } else { - return Biome.OCEAN; - } - } else { - return Biome.SWAMPLAND; - } - } - } - - final int HILLS_HEIGHT = WORLD_SEA_LEVEL + (WORLD_MAX_GENERATION_HEIGHT - WORLD_SEA_LEVEL) / 3; - - if (temperature == Temperature.FROST) { - if (wetness == Wetness.DRIEST || wetness == Wetness.DRY) { - return Biome.COLD_TAIGA; - } else { - if (height > HILLS_HEIGHT) { - return Biome.ICE_MOUNTAINS; - } else { - return Biome.ICE_PLAINS; - } - } - } - - if (wetness == Wetness.DRIEST) { - if (temperature == Temperature.COLD || temperature == Temperature.WARM) { - return Biome.PLAINS; - } else { - if (height > HILLS_HEIGHT) { - return Biome.DESERT_HILLS; - } else { - return Biome.DESERT; - } - } - } - - if (temperature == Temperature.COLD) { - if (wetness == Wetness.DRY || wetness == Wetness.WET) { - if (height > HILLS_HEIGHT) { - return Biome.TAIGA_HILLS; - } else { - return Biome.TAIGA; - } - } else { - return Biome.SWAMPLAND; - } - } - - if (wetness == Wetness.WETTEST) { - if (temperature == Temperature.WARM) { - return Biome.SWAMPLAND; - } else { - if (height > HILLS_HEIGHT) { - return Biome.JUNGLE_HILLS; - } else { - return Biome.JUNGLE; - } - } - } - - if (wetness == Wetness.WETTER) { - if (temperature == Temperature.WARM) { - if (height > HILLS_HEIGHT) { - return Biome.FOREST_HILLS; - } else { - return Biome.FOREST; - } - } else { - return Biome.SAVANNA_PLATO; - } - } - - if (temperature == Temperature.HOTTEST) { - return Biome.SAVANNA; - } - - if (wetness == Wetness.WET) { - if (height > HILLS_HEIGHT) { - return Biome.FOREST_HILLS; - } else { - return Biome.FOREST; - } - } - - return Biome.PLAINS; - } - - private void smooth (int [][] map) { - final int[][] original = map.clone(); - for (int y = 1; y < map.length - 1; y ++) { - for (int x = 1; x < map[0].length - 1; x ++) { - int mid = 0; - for (int tx = x - 1; tx <= x + 1; tx ++) { - for (int ty = y - 1; ty <= y + 1; ty ++) { - mid += original[tx][ty]; - } - } - map[x][y] = mid / 9; - } - } - } - } - -} \ No newline at end of file diff --git a/generated_world/src/main/java/mc/world/generated_world/generator/SeedRandomGenerator.java b/generated_world/src/main/java/mc/world/generated_world/generator/SeedRandomGenerator.java deleted file mode 100644 index c2ff8e7..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/generator/SeedRandomGenerator.java +++ /dev/null @@ -1,23 +0,0 @@ -package mc.world.generated_world.generator; - -public final class SeedRandomGenerator { - - public static double random (int x, int y, int seed) { - x = Math.abs(x - y) + 1; - y = Math.abs(y - x) + 1; - for (int i = 0; i < 20; i ++) { - int a1 = x % 13; - int a2 = x % 31; - int a3 = x % 89; - int a4 = y % 359; - int a5 = y % 7; - int a6 = y % 313; - int a7 = y % 8461; - int a8 = y % 105467; - int a9 = x % 105943; - y = x + seed; - x += a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; - } - return ((x + y) % 100000) / 100000d; - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java b/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java deleted file mode 100644 index 09c98ea..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/region/RegionImpl.java +++ /dev/null @@ -1,142 +0,0 @@ -package mc.world.generated_world.region; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import mc.core.exception.ResourceUnloadedException; -import mc.core.serialization.IRegionReaderWriter; -import mc.core.serialization.Serializer; -import mc.core.world.*; -import mc.core.world.chunk.Chunk; -import mc.core.world.chunk.ChunkLoader; -import mc.core.world.chunk.ChunkSection; -import mc.world.generated_world.chunk.ChunkSectionProxy; -import mc.world.generated_world.chunk.InMemoryCacheChunkLoader; -import mc.world.generated_world.chunk.ChunkImpl; -import mc.world.generated_world.chunk.ChunkSectionImpl; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.text.MessageFormat; - -import static mc.world.generated_world.WorldConstants.*; - -@Slf4j -public class RegionImpl implements Region{ - @Getter - private final int x; - @Getter - private final int z; - private final ChunkSection[][][] chunkSectionProxies = new ChunkSectionProxy[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE]; - private final Biome[][] biomes = new Biome[WORLD_REGION_SIZE][WORLD_REGION_SIZE]; - private final transient Reference world; - private final Chunk[][] chunks = new Chunk[WORLD_REGION_SIZE/WORLD_CHUNK_SIZE][WORLD_REGION_SIZE/WORLD_CHUNK_SIZE]; - @Autowired - private ChunkLoader chunkLoader; - - public RegionImpl (int x, int z, World world) { - this.x = x; - this.z = z; - this.world = new WeakReference<>(world); - } - - @Override - public Chunk getChunk(int x, int z) { - if (x < 0 || z < 0 || x >= 16 || z >= 16) { - throw new RuntimeException(MessageFormat.format("Invalid chunk coordinates [{0} {1}]", x, z)); - } - - Chunk chunk = chunks[x][z]; - if (chunk == null) { - chunk = new ChunkImpl(x, z, this); - for (int y = 0; y < WORLD_CHUNK_SIZE; y ++) { - chunk.setChunkSection(y, getChunkAt(x, y, z)); - } - } - return chunk; - } - - @Override - public void setChunk(int x, int z, Chunk chunk) { - chunks[x][z] = chunk; - } - - @Override - public ChunkSection getChunkAt(int x, int y, int z) { - if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) { - throw new RuntimeException(MessageFormat.format("Invalid chunkSection coordinates [{0} {1} {2}]", x, y, z)); - } - if (chunkLoader == null) { - chunkLoader = new InMemoryCacheChunkLoader(getWorld()); - } - ChunkSection chunkSection = chunkSectionProxies[x][y][z]; - if (chunkSection == null) { - chunkSection = chunkLoader.loadChunk(x + this.x * WORLD_REGION_SIZE, y, this.z * WORLD_REGION_SIZE).orElse(new ChunkSectionImpl(x, y, z, this)); - chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection); - } - return chunkSection; - } - - @Override - public void setChunk(int x, int y, int z, ChunkSection chunkSection) { - if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= 16 || z >= 16) { - throw new RuntimeException(MessageFormat.format("Invalid chunkSection coordinates [{0} {1} {2}]", x, y, z)); - } - chunkSectionProxies[x][y][z] = new ChunkSectionProxy(chunkSection); - } - - @Override - public Biome getBiomeAt(int x, int z) { - if (x < 0 || z < 0 || x >= 256 || z >= 256) { - throw new RuntimeException(MessageFormat.format("Invalid biome coordinates [{0} {1}]", x, z)); - } - return biomes[x][z]; - } - - @Override - public void setBiome(int x, int z, Biome biome) { - if (x < 0 || z < 0 || x >= 256 || z >= 256) { - throw new RuntimeException(MessageFormat.format("Invalid biome coordinates [{0} {1}]", x, z)); - } - biomes[x][z] = biome; - } - - @Override - public World getWorld() { - if (world == null) { - return null; - } - if (world.get() == null) { - throw new ResourceUnloadedException("World is unloaded"); - } - return world.get(); - } - - @Override - public void save(Serializer chunkSerializer, IRegionReaderWriter regionReaderWriter) throws IOException { - String worldPath = System.getProperty("worlds.folder", "worlds"); - File worldFile = new File(worldPath, getWorld().getWorldId().toString()); - File regionFile = new File(worldFile, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, this.getX(), this.getZ())); - if (!regionFile.exists()) { - regionFile.mkdirs(); - } - regionReaderWriter.write(this); - for (int x = 0; x < WORLD_CHUNK_SIZE; x ++) { - for (int z = 0; z < WORLD_CHUNK_SIZE; z ++) { - for (int y = 0; y < WORLD_CHUNK_SIZE; y++) { - ChunkSection chunkSection = this.getChunkAt(x, y, z); - byte[] chunkBytes = chunkSerializer.serialize(chunkSection); - if (chunkBytes.length > 0) { - File chunkFile = new File(regionFile, MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z)); - try (FileOutputStream fileOutputStream = new FileOutputStream(chunkFile)) { - fileOutputStream.write(chunkBytes); - } - } - } - } - } - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java deleted file mode 100644 index 8d05e93..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/BlockSerializerDeserializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package mc.world.generated_world.serialization; - -import mc.core.world.block.Block; -import mc.core.world.block.BlockFactory; -import mc.core.world.block.BlockType; -import mc.core.serialization.Deserializer; -import mc.core.serialization.Serializer; -import mc.core.world.chunk.ChunkSection; - -/** - * Prototype - */ -public class BlockSerializerDeserializer implements Serializer, Deserializer { - - private BlockFactory blockFactory; - private ChunkSection chunkSection; - - public BlockSerializerDeserializer(BlockFactory blockFactory, ChunkSection chunkSection) { - this.blockFactory = blockFactory; - this.chunkSection = chunkSection; - } - - @Override - public Block deserialize(byte[] bytes) { - int id = bytes[0] + 128; - int meta = bytes[1] >> 4; - int x = (bytes[1] & 0xf) + chunkSection.getX() * 16; - int y = bytes[2] >> 4 + chunkSection.getY() * 16; - int z = (bytes[2] & 0xf) + chunkSection.getZ() * 16; - BlockType type = BlockType.values()[id]; - Block block = blockFactory.create(type, meta); - block.getLocation().setX(x); - block.getLocation().setY(y); - block.getLocation().setZ(z); - return block; - } - - @Override - public byte[] serialize(Block block) { - byte[] bytes = new byte[3]; - bytes[0] = (byte) ((block.getId() - 128) & 0xff); - bytes[1] = (byte) ((block.getMeta() << 4) | (block.getLocation().getBlockX() % 16)); - bytes[2] = (byte) (((block.getLocation().getBlockZ() % 16) << 4) | (block.getLocation().getBlockZ() % 16)); - return bytes; - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java deleted file mode 100644 index 4672954..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkReader.java +++ /dev/null @@ -1,47 +0,0 @@ -package mc.world.generated_world.serialization; - -import mc.core.world.block.Block; -import mc.core.serialization.Deserializer; -import mc.core.serialization.IChunkReader; -import mc.core.world.chunk.ChunkSection; -import mc.core.world.Region; -import mc.world.generated_world.chunk.ChunkSectionImpl; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.text.MessageFormat; - -import static mc.world.generated_world.WorldConstants.*; - -public class ChunkReader implements IChunkReader{ - private final File worldFolder; - @Autowired - private Deserializer blockDeserializer; - - public ChunkReader (File worldFolder) { - this.worldFolder = worldFolder; - } - - @Override - public ChunkSection read (Region region, int x, int y, int z) throws IOException { - x %= WORLD_REGION_SIZE; - y %= WORLD_REGION_SIZE; - z %= WORLD_REGION_SIZE; - File chunkFile = new File(new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ())), MessageFormat.format(CHUNK_FILE_NAME_TEMPLATE, x, y, z)); - byte[] chunkBytes = Files.readAllBytes(Paths.get(chunkFile.toURI())); - int blocks = (chunkBytes.length) / 3; - ChunkSection chunkSection = new ChunkSectionImpl(x, y, z, region); - for (int i = 0; i < blocks; i ++) { - byte[] blockBytes = new byte[3]; - blockBytes[0] = chunkBytes[3 * i]; - blockBytes[1] = chunkBytes[1 + 3 * i]; - blockBytes[2] = chunkBytes[2 + 3 * i]; - Block block = blockDeserializer.deserialize(blockBytes); - chunkSection.setBlock(block); - } - return chunkSection; - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java b/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java deleted file mode 100644 index c636a5a..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/ChunkSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -package mc.world.generated_world.serialization; - -import lombok.extern.slf4j.Slf4j; -import mc.core.world.block.Block; -import mc.core.world.block.BlockFactory; -import mc.core.world.block.BlockType; -import mc.core.serialization.Serializer; -import mc.core.world.chunk.ChunkSection; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import static mc.world.generated_world.WorldConstants.WORLD_CHUNK_SIZE; - -@Slf4j -public class ChunkSerializer implements Serializer { - - @Autowired - private Serializer blockSerializer; - - @Override - public byte[] serialize(ChunkSection chunkSection) { - Serializer blockSerializer = new BlockSerializerDeserializer(new BlockFactory(), chunkSection); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Block current; - for (int x = 0; x < WORLD_CHUNK_SIZE; x ++) { - for (int y = 0; y < WORLD_CHUNK_SIZE; y ++) { - for (int z = 0; z < WORLD_CHUNK_SIZE; z ++) { - current = chunkSection.getBlock(x, y, z); - if (current != null && current.getBlockType() != BlockType.AIR) { - try { - baos.write(blockSerializer.serialize(current)); - } catch (IOException e) { - log.error("Error occurred while writing serialized block to byte array", e); - } - } - } - } - } - return baos.toByteArray(); - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java b/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java deleted file mode 100644 index 20595a4..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/RegionReaderWriter.java +++ /dev/null @@ -1,56 +0,0 @@ -package mc.world.generated_world.serialization; - -import mc.core.serialization.IRegionReaderWriter; -import mc.core.world.Biome; -import mc.core.world.Region; -import mc.core.world.World; -import mc.world.generated_world.region.RegionImpl; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.text.MessageFormat; - -import static mc.world.generated_world.WorldConstants.*; - -public class RegionReaderWriter implements IRegionReaderWriter { - private final File worldFolder; - - public RegionReaderWriter(File worldFolder) { - this.worldFolder = worldFolder; - } - - @Override - public Region read (int x, int z, World world) throws IOException{ - File regionFolder = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z)); - File biomesFile = new File(regionFolder, BIOME_FILE_NAME_TEMPLATE); - byte[] biomesBytes = Files.readAllBytes(Paths.get(biomesFile.toURI())); - Region region = new RegionImpl(x, z, world); - for (int tx = 0; tx < WORLD_REGION_SIZE; tx ++) { - for (int tz = 0; tz < WORLD_REGION_SIZE; tz ++) { - region.setBiome(tx, tz, Biome.getById(biomesBytes[tx * WORLD_REGION_SIZE + tz])); - } - } - return region; - } - - @Override - public void write (Region region) throws IOException{ - File regionFolder = new File(worldFolder, MessageFormat.format(REGION_FILE_NAME_TEMPLATE, region.getX(), region.getZ())); - if (!regionFolder.exists()) { - regionFolder.mkdirs(); - } - File biomesFile = new File(regionFolder, BIOME_FILE_NAME_TEMPLATE); - byte[] biomesBytes = new byte[WORLD_REGION_SIZE * WORLD_REGION_SIZE]; - for (int x = 0; x < WORLD_REGION_SIZE; x ++) { - for (int z = 0; z < WORLD_REGION_SIZE; z ++) { - biomesBytes[x * WORLD_REGION_SIZE + z] = (byte) region.getBiomeAt(x, z).getId(); - } - } - try (FileOutputStream fos = new FileOutputStream(biomesFile)) { - fos.write(biomesBytes); - } - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/serialization/WorldReaderWriter.java b/generated_world/src/main/java/mc/world/generated_world/serialization/WorldReaderWriter.java deleted file mode 100644 index 9aa127e..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/serialization/WorldReaderWriter.java +++ /dev/null @@ -1,62 +0,0 @@ -package mc.world.generated_world.serialization; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import mc.core.EntityLocation; -import mc.core.world.World; -import mc.world.generated_world.world.CubicWorld; - -import java.io.*; -import java.util.UUID; - -import static mc.world.generated_world.WorldConstants.WORLD_INFO_FILE_NAME_TEMPLATE; - -@Slf4j -public class WorldReaderWriter { - private final File worldsFolder; - - public WorldReaderWriter(File worldsFolder) { - this.worldsFolder = worldsFolder; - } - - public World readWorld (UUID uuid) throws IOException { - World world = null; - File worldFolder = new File(worldsFolder, uuid.toString()); - if (!worldFolder.exists()) { - throw new FileNotFoundException("World folder is not exist"); - } - File worldInfoFile = new File(worldFolder, WORLD_INFO_FILE_NAME_TEMPLATE); - WorldInfo worldInfo; - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(worldInfoFile))) { - worldInfo = (WorldInfo) ois.readObject(); - } catch (ClassNotFoundException e) { - log.error("Error occurred while reading world info file", e); - return null; - } - world = new CubicWorld(uuid, worldInfo.getSeed()); - world.setSpawn(worldInfo.getSpawn()); - world.setName(worldInfo.getName()); - return world; - } - - public void writeWorldInfo (World world) throws IOException { - File worldFolder = new File(worldsFolder, world.getWorldId().toString()); - worldFolder.mkdirs(); - File worldInfoFile = new File(worldFolder, WORLD_INFO_FILE_NAME_TEMPLATE); - WorldInfo worldInfo = new WorldInfo(); - worldInfo.setName(world.getName()); - worldInfo.setSeed(world.getSeed()); - worldInfo.setSpawn(world.getSpawn()); - try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(worldInfoFile))) { - oos.writeObject(worldInfo); - oos.flush(); - } - } - - @Data - public static class WorldInfo implements Serializable { - private EntityLocation spawn; - private String name; - private int seed; - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java b/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java deleted file mode 100644 index f33eade..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/world/CubicWorld.java +++ /dev/null @@ -1,225 +0,0 @@ -package mc.world.generated_world.world; - -import com.flowpowered.nbt.Tag; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import mc.core.Direction; -import mc.core.EntityLocation; -import mc.core.world.*; -import mc.core.world.chunk.ChunkSection; -import mc.world.generated_world.serialization.RegionReaderWriter; -import org.springframework.beans.factory.annotation.Autowired; - -import java.io.File; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Stream; - -import static mc.world.generated_world.WorldConstants.REGION_FILE_NAME_TEMPLATE; - -/* - * NORTH - * - * EAST WEST - * - * SOUTH - * - * + ----> X - * | - * | - * | - * V Z - */ - -@Slf4j -public class CubicWorld implements World { - private int pointX = -1; - private int pointZ = -1; - private int sizeX = 2; - private int sizeZ = 2; - private Region[][] regions = new Region[sizeX][sizeZ]; - private final Lock regionSaveLock = new ReentrantLock(); - @Autowired - private RegionReaderWriter regionReaderWriter; - @Autowired - private WorldGenerator worldGenerator; - @Setter - private boolean autoSaveRegionAfterGenerating = true; - @Getter - private final UUID worldId; - private final int seed; - private volatile EntityLocation spawn; - private final transient Object spawnLocationLock = new Object(); - private final Map> nbtTagMap = new HashMap<>(); - @Getter@Setter - private String name; - - public CubicWorld(UUID worldId, int seed) { - this.worldId = worldId; - this.seed = seed; - } - - public CubicWorld(int seed) { - this.worldId = UUID.randomUUID(); - this.seed = seed; - } - - public CubicWorld(UUID worldId) { - this.worldId = worldId; - this.seed = 0; - } - - public CubicWorld () { - this.worldId = UUID.randomUUID(); - this.seed = 0; - } - - @Override - public IWorldType getWorldType() { - return null; //FIXME - } - - @Override - public EntityLocation getSpawn() { - /* FIXME */ - if (spawn == null) { - log.warn("Spawn is not defined! Set default spawn: [8, 128, 8]"); - setSpawn(new EntityLocation(8d, 128d, 8d, 0f, 0f, this)); - } - return spawn; - } - - @Override - public void setSpawn(EntityLocation entityLocation) { - synchronized (spawnLocationLock) { - entityLocation.setWorld(this); - this.spawn = entityLocation; - } - } - - @Override - public ChunkSection getChunk(int x, int y, int z) { - Region region = getRegion(x / 16, z / 16); - return region.getChunkAt(x % 16, y % 16, z % 16); - } - - @Override - public void setChunk(int x, int y, int z, ChunkSection chunkSection) { - throw new UnsupportedOperationException(); - } - - @Override - public Region getRegion(int x, int z) { - checkCoordsInCache(x, z); - Region region; - if (regions[x - pointX][z - pointZ] == null) { - File file = new File(new File("worlds", this.getWorldId().toString()), MessageFormat.format(REGION_FILE_NAME_TEMPLATE, x, z)); - if (!file.exists()) { - region = worldGenerator.generateRegion(x, z, this); - if (autoSaveRegionAfterGenerating) { - try { - regionReaderWriter.write(region); - } catch (IOException e) { - log.error("Error occurred while saving region data"); - } - } - } else { - try { - region = regionReaderWriter.read(x, z, this); - } catch (IOException e) { - log.error("Error occurred while loading region"); - region = null; - } - } - setRegion(region.getX(), region.getZ(), region); - } else { - region = regions[x - pointX][z - pointZ]; - } - return region; - } - - @Override - public void setRegion(int x, int z, Region region) { - try { - regionSaveLock.lock(); - regions[x - pointX][z - pointZ] = region; - } finally { - regionSaveLock.unlock(); - } - } - - @Override - public int getSeed() { - return seed; - } - - @Override - public Tag getTag(String name) { - return nbtTagMap.get(name); - } - - @Override - public void setTag(Tag tag) { - nbtTagMap.put(tag.getName(), tag); - } - - @Override - public Stream> tagStream() { - return nbtTagMap.values().stream(); - } - - private void checkCoordsInCache (int x, int z) { - if (x < pointX) { - addLines(Direction.EAST, pointX - x); - } else if (x > pointX + sizeX) { - addLines(Direction.WEST, x - (pointX + sizeX)); - } else if (z < pointZ) { - addLines(Direction.NORTH, pointZ - z); - } else if (z > pointZ + sizeZ) { - addLines(Direction.SOUTH, z - (pointZ + sizeZ)); - } - } - - private void addLines (Direction direction, int amount) { - int addBeforeX = 0; - int addAfterX = 0; - int addBeforeZ = 0; - int addAfterZ = 0; - switch (direction) { - case NORTH: - addBeforeZ = amount; - break; - case EAST: - addBeforeX = amount; - break; - case WEST: - addAfterX = amount; - break; - case SOUTH: - addAfterZ = amount; - break; - } - try { - regionSaveLock.lock(); - int tempSizeX = sizeX + addAfterX + addBeforeX; - int tempSizeZ = sizeZ + addAfterZ + addBeforeZ; - Region[][] temp = new Region[tempSizeX][tempSizeZ]; - for (int x = 0; x < sizeX; x ++) { - System.arraycopy(regions[x], 0, temp[x + addBeforeX], addBeforeZ, sizeZ); - } - - this.sizeX = tempSizeX; - this.sizeZ = tempSizeZ; - this.pointX = pointX - addBeforeX; - this.pointZ = pointZ - addBeforeZ; - } finally { - regionSaveLock.unlock(); - } - } -} diff --git a/generated_world/src/main/java/mc/world/generated_world/world/Temperature.java b/generated_world/src/main/java/mc/world/generated_world/world/Temperature.java deleted file mode 100644 index 52b48c3..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/world/Temperature.java +++ /dev/null @@ -1,9 +0,0 @@ -package mc.world.generated_world.world; - -public enum Temperature { - FROST, - COLD, - WARM, - HOT, - HOTTEST -} diff --git a/generated_world/src/main/java/mc/world/generated_world/world/Wetness.java b/generated_world/src/main/java/mc/world/generated_world/world/Wetness.java deleted file mode 100644 index a1ed1ce..0000000 --- a/generated_world/src/main/java/mc/world/generated_world/world/Wetness.java +++ /dev/null @@ -1,10 +0,0 @@ -package mc.world.generated_world.world; - -public enum Wetness { - DRIEST, - DRY, - WET, - WETTER, - WETTEST, - WATER -} diff --git a/generated_world/src/main/resources/log4j2.xml b/generated_world/src/main/resources/log4j2.xml deleted file mode 100644 index 0ea354b..0000000 --- a/generated_world/src/main/resources/log4j2.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java b/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java deleted file mode 100644 index 6c5fbe4..0000000 --- a/generated_world/src/test/java/mc/world/generated_world/SeedRandomGeneratorTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package mc.world.generated_world; - -import mc.world.generated_world.generator.SeedRandomGenerator; -import org.junit.Ignore; -import org.junit.Test; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; - -import static org.junit.Assert.*; - -@Ignore -public class SeedRandomGeneratorTest { - - @Test - public void randomGenSpeed () { - SeedRandomGenerator.random(0, 0, 0); - long avg = 0; - long min = -1; - long max = 0; - for (int i = 0; i < 500; i ++) { - int x = (int) (Math.random() * 10000); - int y = (int) (Math.random() * 10000); - int seed = (int) (Math.random() * 10000); - long time = System.nanoTime(); - SeedRandomGenerator.random(x, y, seed); - time = System.nanoTime() - time; - System.out.printf("[%s] \t%.3fms\n", i+1, time/1000d); - avg += time; - if (min == -1) { - min = time; - } else if (min > time) { - min = time; - } - if (max < time) { - max = time; - } - } - System.out.println(); - System.out.printf("Average time: %.3fms\n", avg/500000d); - System.out.printf("Minimum time: %.3fms\n", min/1000d); - System.out.printf("Maximum time: %.3fms\n", max/1000d); - assertTrue(avg/500 < 5000); - } - - @Test - public void randomTest() throws Exception { - double maxDiff = 0; - double maxDisp = 0; - for (int i = 0; i < 100; i ++) { - double mid = 0; - double disp = 0; - int seed = (int) (Math.random() * Integer.MAX_VALUE); - for (int x = -1000; x < 1000; x++) { - for (int y = -1000; y < 1000; y++) { - double rnd = SeedRandomGenerator.random(x, y, seed); - mid += rnd; - disp += (rnd - 0.5) * (rnd - 0.5); - } - } - mid = mid/4000000; - disp = Math.sqrt(disp)/4000000; - if (maxDiff < Math.abs(mid - 0.5)) { - maxDiff = Math.abs(mid - 0.5); - } - if (maxDisp < disp) { - maxDisp = disp; - } - System.out.printf("Iteration %d.\t mid: %.3f, \tdisp %.6f\n", i + 1, mid, disp); - assertTrue(Math.abs(mid - 0.5) < 0.15); - } - System.out.printf("Max diff: %.3f\n", maxDiff); - System.out.printf("Max disp: %.6f\n", maxDisp); - - - assertTrue(maxDiff > 0); - } - - @Test - public void generateImage () throws Exception { - int h = 500; - int w = 500; - BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); - - int seed = (int) (Math.random() * Integer.MAX_VALUE) / 1024; - for (int x = 0; x < w; x ++) { - for (int y = 0; y < h; y ++) { - image.setRGB(x, y, (int) (0xffffff * SeedRandomGenerator.random(x, y, seed))); - } - } - ImageIO.write(image, "bmp", new File("out", "seed_random.png")); - } - -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bc4f7bf..52ad5e4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,5 +5,3 @@ include('flat_world') include('vanilla_commands') include('proto_1.12.2') // Protocol 1.12.2 include('proto_1.12.2_netty') // Protocol 1.12.2 (Netty impl.) -include('generated_world') -include('event-loop') \ No newline at end of file