Compare commits

...
This repository has been archived on 2022-10-09. You can view files and clone it, but cannot push or open issues or pull requests.

15 Commits

Author SHA1 Message Date
8c7c37c9c0 Code cleanup 2021-07-14 18:23:10 +03:00
3b24ee0531 Documentation for module system and minor changes. 2021-07-10 21:46:15 +03:00
953614a7d8 Now you cannot run tasks second time or if requirements are not met 2021-07-10 20:19:57 +03:00
7f57510ac8 Licensed module system. Minor changes
- Added license commetary to each file of module system
- Added default meta info generation for Module
2021-07-10 08:29:09 +03:00
c7842935ba Loaders are observable. Modules refactoring
- Now it is possible to monitor loaders and tasks they perform
- Now it is NOT possible to add  dublicate tasks and modules
2021-07-09 18:12:30 +03:00
f520d4b2c6 TaskManager bug fixes and code cleanup 2021-07-07 17:02:38 +03:00
5a06788652 Refactored modules system 2021-07-07 13:37:11 +03:00
56e9be727b Removed debug code 2021-07-05 11:32:58 +03:00
e795d76367 Update .gitignore
- added log files to ignore
2021-07-05 11:32:11 +03:00
a9724d9d4c Developed module loader algorithm 2021-06-30 19:59:30 +03:00
3859db5b27 Added TaskManager
- Working on modules system
2021-06-27 18:27:56 +03:00
513feb1093 Merge branch 'master' into moduleSystem 2021-06-12 21:05:15 +03:00
942c665d73 Developing module systme
- Added Module class
- minor changes in Task class
2021-01-10 15:30:05 +03:00
5a521fc131 Refactored Task class
- Now crash reports have stacktrace
- Minor changes
2021-01-09 20:52:19 +03:00
361d795ab1 Added Task class 2021-01-09 19:36:11 +03:00
8 changed files with 419 additions and 6 deletions

1
.gitignore vendored
View File

@ -37,3 +37,4 @@ build_packages/NSIS/*
!build_packages/DEB !build_packages/DEB
build_packages/DEB/* build_packages/DEB/*
!build_packages/DEB/template !build_packages/DEB/template
*.log

View File

@ -111,6 +111,8 @@ switch (OperatingSystem.current()) {
} }
dependencies { dependencies {
implementation 'org.jetbrains:annotations:20.1.0'
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion") implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
implementation "org.lwjgl:lwjgl" implementation "org.lwjgl:lwjgl"

View File

@ -28,6 +28,7 @@ import ru.windcorp.progressia.client.graphics.font.Typefaces;
import ru.windcorp.progressia.client.graphics.texture.Atlases; import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.localization.Localizer; import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.modules.TaskManager;
import ru.windcorp.progressia.common.resource.ResourceManager; import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.server.ServerState; import ru.windcorp.progressia.server.ServerState;
@ -57,6 +58,8 @@ public class ClientProxy implements Proxy {
Atlases.loadAllAtlases(); Atlases.loadAllAtlases();
AudioSystem.initialize(); AudioSystem.initialize();
TaskManager.getInstance().startLoading();
ServerState.startServer(); ServerState.startServer();
ClientState.connectToLocalServer(); ClientState.connectToLocalServer();

View File

@ -18,14 +18,28 @@
package ru.windcorp.progressia.client.audio; package ru.windcorp.progressia.client.audio;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.common.modules.Module;
import ru.windcorp.progressia.common.modules.Task;
import ru.windcorp.progressia.common.modules.TaskManager;
import ru.windcorp.progressia.common.resource.ResourceManager; import ru.windcorp.progressia.common.resource.ResourceManager;
public class AudioSystem { public class AudioSystem {
static public void initialize() { static public void initialize() {
Module audioModule = new Module("AudioModule:System");
AudioManager.initAL(); AudioManager.initAL();
Thread shutdownHook = new Thread(AudioManager::closeAL, "AL Shutdown Hook"); Thread shutdownHook = new Thread(AudioManager::closeAL, "AL Shutdown Hook");
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
Task t = new Task("AudioSystem:Initialize") {
@Override
protected void perform() {
loadAudioData(); loadAudioData();
LogManager.getLogger().info("Audio data is loaded");
}
};
audioModule.addTask(t);
TaskManager.getInstance().registerModule(audioModule);
} }
static void loadAudioData() { static void loadAudioData() {

View File

@ -0,0 +1,59 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.modules;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import java.util.*;
public class Module extends Namespaced {
private final Set<Task> tasks = new HashSet<>();
private final Map<String, String> meta = new HashMap<>();
/**
* @param id the identifier of a task object.
* Its format is restricted by {@link Namespaced}.
* @see Namespaced#Namespaced
*/
public Module(String id) {
super(id);
meta.put("id", id);
}
/**
* @return meta information of the module as {@link Map}.
*/
public Map<String, String> getMeta() {
return Collections.unmodifiableMap(meta);
}
public Set<Task> getTasks() {
return Collections.unmodifiableSet(tasks);
}
/**
* @param task that will be attached to the module.
* A task can't be added to any module second time.
*/
public void addTask(Task task) {
task.setOwner(this);
tasks.add(task);
}
}

View File

@ -0,0 +1,31 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.modules;
public class ModuleBuilder {
private final Module module;
public ModuleBuilder(String id) {
module = new Module(id);
}
public ModuleBuilder AddTask(Task task) {
module.addTask(task);
return this;
}
}

View File

@ -0,0 +1,130 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.modules;
import ru.windcorp.jputil.chars.StringUtil;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class Task
extends Namespaced
implements Runnable {
private final Set<Task> requiredTasks = new HashSet<>();
private final AtomicBoolean isDone = new AtomicBoolean(false);
private final AtomicBoolean isActive = new AtomicBoolean(false);
private Module owner;
/**
* @param id the identifier of a task object.
* Its format is restricted by {@link Namespaced}.
* @see Namespaced#Namespaced
*/
protected Task(String id) {
super(id);
}
@Override
public void run() {
if (!canRun()) {
List<Task> undoneTasks = new ArrayList<>();
for (Iterator<Task> iterator = requiredTasks.iterator(); iterator.hasNext(); ) {
Task t = iterator.next();
if (!t.isDone()) {
undoneTasks.add(t);
}
}
throw CrashReports.report(new Throwable(),
"The following required Tasks are not done:\n%s",
StringUtil.iterableToString(undoneTasks, "\n"));
} else if (isDone()) {
throw CrashReports.report(new Throwable(),
"The task cannot be performed second time");
} else {
isActive.set(true);
perform();
isActive.set(false);
isDone.set(true);
}
}
/**
* The method is to be invoked in run().
* @see Task#run()
*/
protected abstract void perform();
public boolean isDone() {
return isDone.get();
}
/**
* @return if the {@link Task#run()} method is being invoked at the moment or not.
*/
public boolean isActive() {
return isActive.get();
}
/**
*
* @return true - the method is not done and not active
* and all requirement tasks are done, false - otherwise.
*/
public boolean canRun() {
if (this.isActive.get() || isDone.get()) return false;
for (Iterator<Task> iterator = requiredTasks.iterator(); iterator.hasNext(); ) {
Task reqTask = iterator.next();
if (!reqTask.isDone()) return false;
}
return true;
}
public Set<Task> getRequiredTasks() {
return Collections.unmodifiableSet(requiredTasks);
}
public void addRequiredTask(Task task) {
requiredTasks.add(task);
}
/**
* @return the module the task is attached to.
*/
public Module getOwner() {
return owner;
}
/**
* @param module to which the task will be attached.
* Only one module can be the owner of the task.
*/
public void setOwner(Module module) {
if (owner != null) {
throw CrashReports.crash(
null
, "Could not set %s as owner of %s, because %s is already owner of it.",
module.getId(), this.getId(), this.owner.getId());
} else {
owner = module;
}
}
}

View File

@ -0,0 +1,173 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.modules;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.Executors.newFixedThreadPool;
public final class TaskManager {
private static final TaskManager INSTANCE = new TaskManager();
private final Set<Task> tasks = new HashSet<>();
private final Set<Module> modules = new HashSet<>();
private final ExecutorService executorService;
final AtomicBoolean loadingDone;
final AtomicInteger activeThreadsCount;
private final Map<Thread, Namespaced> loadersMonitorMap;
private TaskManager() {
loadingDone = new AtomicBoolean(false);
activeThreadsCount = new AtomicInteger(0);
executorService = newFixedThreadPool(
Runtime.getRuntime().availableProcessors(), Thread::new);
loadersMonitorMap = new HashMap<>(Runtime.getRuntime().availableProcessors());
}
public static TaskManager getInstance() {
return TaskManager.INSTANCE;
}
/**
* Registers the module and its tasks that are
* to be performed by {@link TaskManager#startLoading()}.
*
* @param module from where to register tasks for loading.
*/
public void registerModule(Module module) {
tasks.addAll(module.getTasks());
modules.add(module);
}
/**
* Registers a task that is to be performed
* by {@link TaskManager#startLoading()}.
*
* @param task to register for loading.
*/
public void addTask(Task task) {
tasks.add(task);
}
public boolean isLoadingDone() {
return loadingDone.get();
}
/**
* The method to start loading. It will perform every registered task.
*/
public void startLoading() {
LogManager.getLogger().info("Loading is started");
int procAmount = Runtime.getRuntime().availableProcessors();
for (int i = 0; i < procAmount; i++) {
executorService.submit(loaderTask);
}
waitForLoadingEnd();
if (!tasks.isEmpty()) {
throw CrashReports.crash(null, "Loading is failed");
}
LogManager.getLogger().info("Loading is finished");
executorService.shutdownNow();
}
/**
* @return Task - founded registered task with {@link Task#canRun()} = true;
* null - there is no available task found.
* @see Task#canRun()
*/
synchronized Task getRunnableTask() {
if (!tasks.isEmpty()) {
for (Iterator<Task> iterator = tasks.iterator(); iterator.hasNext(); ) {
Task t = iterator.next();
if (t.canRun()) {
tasks.remove(t);
return t;
}
}
}
return null;
}
/**
* Makes the thread that is performing this method
* to wait until the loading is not done.
*/
private void waitForLoadingEnd() {
synchronized (tasks) {
while (!loadingDone.get()) {
try {
tasks.wait();
} catch (InterruptedException ignored) {}
}
}
}
private void stopLoading() {
loadingDone.set(true);
synchronized (tasks) {
tasks.notifyAll();
}
}
/**
* @return a map where key is a thread making loading
* and where value is a {@link Namespaced} of {@link Task} that is being performed by it
* at the moment.
* @see Namespaced
*/
public Map<Thread, Namespaced> getLoadersMonitorMap() {
return Collections.unmodifiableMap(loadersMonitorMap);
}
private final Runnable loaderTask = new Runnable() {
@Override
public void run() {
while (!loadingDone.get()) {
Task t = getRunnableTask();
if (t != null) {
activeThreadsCount.incrementAndGet();
loadersMonitorMap.put(Thread.currentThread(), t);
t.run();
loadersMonitorMap.put(Thread.currentThread(), null);
activeThreadsCount.decrementAndGet();
synchronized (tasks) {
tasks.notifyAll();
}
} else if (activeThreadsCount.get() > 0) {
try {
synchronized (tasks) {
tasks.wait();
}
} catch (InterruptedException ignored) {}
} else {
stopLoading();
}
}
}
};
}