Skip to content

Commit 4c3e0d2

Browse files
committed
Initial commit
0 parents  commit 4c3e0d2

File tree

76 files changed

+1883
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1883
-0
lines changed

ClassiCubeJavaLoader.c

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
#ifdef _WIN32
2+
#define CC_API __declspec(dllimport)
3+
#define CC_VAR __declspec(dllimport)
4+
#define EXPORT __declspec(dllexport)
5+
#else
6+
#define CC_API
7+
#define CC_VAR
8+
#define EXPORT __attribute__((visibility("default")))
9+
#endif
10+
11+
#include <math.h>
12+
13+
#include <windows.h>
14+
#include <jni.h>
15+
16+
#include "..\src\Chat.h"
17+
#include "..\src\Game.h"
18+
#include "..\src\String.h"
19+
#include "..\src\Event.h"
20+
21+
#define JLMOD "ClassiCubeJavaLoaderBridge.jar"
22+
#define JVMDLL "bin\\server\\jvm.dll"
23+
#define SHOULD_LOAD_VCRUNTIME true
24+
#define VCRUNTIME "bin\\vcruntime140.dll"
25+
#define SHOULD_LOAD_MSVCP true
26+
#define MSVCP "bin\\msvcp140.dll"
27+
#define STR_BUFFER_LENGTH 1024
28+
#define LOADER_TICK_FREQUENCY 144
29+
#define LOADER_TICK_EVENT_ID 0
30+
#define SPECIAL_EVENTS_OFFSET 10000
31+
#define SCHEDULED_TASKS_OFFSET 20000
32+
33+
typedef jint(JNICALL* CreateJavaVM)(JavaVM**, void**, void*);
34+
35+
static JavaVM* jvm = NULL;
36+
static JNIEnv* env = NULL;
37+
static jclass mainClass = NULL;
38+
39+
#pragma region Utils
40+
41+
BOOL FileExists(LPCSTR path) {
42+
DWORD attrubutes = GetFileAttributesA(path);
43+
44+
return (attrubutes != INVALID_FILE_ATTRIBUTES && !(attrubutes & FILE_ATTRIBUTE_DIRECTORY));
45+
}
46+
47+
static char* Concatenate(const char* first, const char* second) {
48+
size_t newLength = strlen(first) + strlen(second) + 1;
49+
char* newStr = (char*)malloc(newLength);
50+
if (newStr == NULL) return NULL;
51+
52+
strcpy_s(newStr, newLength, first);
53+
strcat_s(newStr, newLength, second);
54+
// newStr[newLength - 1] = 0; // should not be needed: strcat is expected to put the terminator
55+
56+
return newStr;
57+
}
58+
59+
static cc_bool LoadJavaLibrary(const char* javaHomePath, cc_bool jre, const char* path) {
60+
char* relativePath = (jre ? Concatenate("jre\\", path) : path);
61+
char* libraryPath = Concatenate(javaHomePath, relativePath);
62+
HINSTANCE hModule = LoadLibraryA(libraryPath);
63+
free(libraryPath);
64+
if (jre) free(relativePath);
65+
66+
return hModule != NULL;
67+
}
68+
69+
static jmethodID GetStaticMethodId(const char* name, const char* signature) {
70+
return (*env)->GetStaticMethodID(env, mainClass, name, signature);
71+
}
72+
73+
static jobject CallStaticObjectJava(const char* name, const char* signature) {
74+
jmethodID method = GetStaticMethodId(name, signature);
75+
if (method == NULL) return NULL;
76+
77+
return (*env)->CallStaticObjectMethod(env, mainClass, method);
78+
}
79+
80+
static void FireEvent(void* obj) {
81+
jint eventId = (jint) obj;
82+
jmethodID fireEvent = (*env)->GetStaticMethodID(env, mainClass, "fireEvent", "(I)V");
83+
if (fireEvent == NULL) return;
84+
(*env)->CallStaticVoidMethod(env, mainClass, fireEvent, eventId);
85+
}
86+
87+
#pragma endregion Utils
88+
89+
static void PerformScheduledTask(struct ScheduledTask* task) {
90+
long withTaskId = (long)round(task->interval * 10000000.0);
91+
jint taskId = (jint) (withTaskId % 1000L);
92+
93+
FireEvent(SCHEDULED_TASKS_OFFSET + taskId);
94+
}
95+
96+
static void LoaderTick(struct ScheduledTask* task) {
97+
FireEvent(LOADER_TICK_EVENT_ID);
98+
99+
jobjectArray chatMessages = CallStaticObjectJava("getPendingChatMessages", "()[Ljava/lang/String;");
100+
if (chatMessages == NULL) return;
101+
102+
jsize messageCount = (*env)->GetArrayLength(env, chatMessages);
103+
for (jsize i = 0; i < messageCount; i++) {
104+
jstring string = (*env)->GetObjectArrayElement(env, chatMessages, i);
105+
const char* rawString = (*env)->GetStringUTFChars(env, string, 0);
106+
107+
cc_string message = String_FromReadonly(rawString);
108+
Chat_Add(&message);
109+
110+
(*env)->ReleaseStringUTFChars(env, string, rawString);
111+
}
112+
113+
jintArray scheduledTaskIDs = CallStaticObjectJava("getPendingScheduledTaskIDs", "()[I");
114+
if (scheduledTaskIDs == NULL) return;
115+
116+
jsize pendingCount = (*env)->GetArrayLength(env, scheduledTaskIDs);
117+
118+
jint* taskIDs = (*env)->GetIntArrayElements(env, scheduledTaskIDs, 0);
119+
for (jsize i = 0; i < pendingCount; i++) {
120+
jint taskId = taskIDs[i];
121+
122+
jmethodID method = GetStaticMethodId("getPendingScheduledTaskInterval", "(I)D");
123+
if (method == NULL) return;
124+
jdouble taskInterval = (*env)->CallStaticDoubleMethod(env, mainClass, method);
125+
126+
double newInterval = taskInterval + (0.0000001 * taskId);
127+
ScheduledTask_Add(newInterval, PerformScheduledTask);
128+
}
129+
130+
CallStaticObjectJava("freePendingInfo", "()Ljava.lang.Object;");
131+
(*env)->ReleaseIntArrayElements(env, scheduledTaskIDs, taskIDs, JNI_ABORT);
132+
133+
/*
134+
// todo research why this code didn't work (the game likely crashed on GetDoubleArrayElements call)
135+
136+
jdoubleArray scheduledTaskIntervals = CallStaticObjectJava("getPendingScheduledTaskIntervals", "()[D");
137+
if (scheduledTaskIntervals = NULL) return;
138+
139+
jsize pendingCount = (*env)->GetArrayLength(env, scheduledTaskIDs);
140+
141+
jint* taskIDs = (*env)->GetIntArrayElements(env, scheduledTaskIDs, 0);
142+
jdouble* taskIntervals = (*env)->GetDoubleArrayElements(env, scheduledTaskIntervals, 0);
143+
if (true) return;
144+
for (jsize i = 0; i < pendingCount; i++) {
145+
jint taskId = taskIDs[i];
146+
jdouble taskInterval = taskIntervals[i];
147+
148+
double newInterval = taskInterval + (0.0000001 * taskId);
149+
ScheduledTask_Add(newInterval, PerformScheduledTask);
150+
}
151+
152+
(*env)->ReleaseIntArrayElements(env, scheduledTaskIDs, taskIDs, JNI_ABORT);
153+
(*env)->ReleaseDoubleArrayElements(env, scheduledTaskIntervals, taskIntervals, JNI_ABORT);
154+
*/
155+
}
156+
157+
static void SetupEvents() {
158+
if (EventAPIVersion < 4) {
159+
cc_string eventIssueMsg = String_FromConst("&cFailed to setup events: event API version is less than 4");
160+
Chat_Add(&eventIssueMsg);
161+
162+
return;
163+
}
164+
jint eventId = LOADER_TICK_EVENT_ID + 1;
165+
166+
// automatically generated code
167+
Event_Register(&EntityEvents.Added, eventId++, FireEvent);
168+
Event_Register(&EntityEvents.Removed, eventId++, FireEvent);
169+
Event_Register(&TabListEvents.Added, eventId++, FireEvent);
170+
Event_Register(&TabListEvents.Changed, eventId++, FireEvent);
171+
Event_Register(&TabListEvents.Removed, eventId++, FireEvent);
172+
Event_Register(&TextureEvents.AtlasChanged, eventId++, FireEvent);
173+
Event_Register(&TextureEvents.PackChanged, eventId++, FireEvent);
174+
Event_Register(&TextureEvents.FileChanged, eventId++, FireEvent);
175+
Event_Register(&GfxEvents.ViewDistanceChanged, eventId++, FireEvent);
176+
Event_Register(&GfxEvents.LowVRAMDetected, eventId++, FireEvent);
177+
Event_Register(&GfxEvents.ProjectionChanged, eventId++, FireEvent);
178+
Event_Register(&GfxEvents.ContextLost, eventId++, FireEvent);
179+
Event_Register(&GfxEvents.ContextRecreated, eventId++, FireEvent);
180+
Event_Register(&UserEvents.BlockChanged, eventId++, FireEvent);
181+
Event_Register(&UserEvents.HackPermsChanged, eventId++, FireEvent);
182+
Event_Register(&UserEvents.HeldBlockChanged, eventId++, FireEvent);
183+
Event_Register(&UserEvents.HacksStateChanged, eventId++, FireEvent);
184+
Event_Register(&BlockEvents.PermissionsChanged, eventId++, FireEvent);
185+
Event_Register(&BlockEvents.BlockDefChanged, eventId++, FireEvent);
186+
Event_Register(&WorldEvents.NewMap, eventId++, FireEvent);
187+
Event_Register(&WorldEvents.Loading, eventId++, FireEvent);
188+
Event_Register(&WorldEvents.MapLoaded, eventId++, FireEvent);
189+
Event_Register(&WorldEvents.EnvVarChanged, eventId++, FireEvent);
190+
Event_Register(&WorldEvents.LightingModeChanged, eventId++, FireEvent);
191+
Event_Register(&ChatEvents.FontChanged, eventId++, FireEvent);
192+
Event_Register(&ChatEvents.ChatReceived, eventId++, FireEvent);
193+
Event_Register(&ChatEvents.ChatSending, eventId++, FireEvent);
194+
Event_Register(&ChatEvents.ColCodeChanged, eventId++, FireEvent);
195+
Event_Register(&WindowEvents.RedrawNeeded, eventId++, FireEvent);
196+
Event_Register(&WindowEvents.Resized, eventId++, FireEvent);
197+
Event_Register(&WindowEvents.Closing, eventId++, FireEvent);
198+
Event_Register(&WindowEvents.FocusChanged, eventId++, FireEvent);
199+
Event_Register(&WindowEvents.StateChanged, eventId++, FireEvent);
200+
Event_Register(&WindowEvents.Created, eventId++, FireEvent);
201+
Event_Register(&WindowEvents.InactiveChanged, eventId++, FireEvent);
202+
Event_Register(&WindowEvents.Redrawing, eventId++, FireEvent);
203+
Event_Register(&InputEvents.Press, eventId++, FireEvent);
204+
Event_Register(&InputEvents._down, eventId++, FireEvent);
205+
Event_Register(&InputEvents._up, eventId++, FireEvent);
206+
Event_Register(&InputEvents.Wheel, eventId++, FireEvent);
207+
Event_Register(&InputEvents.TextChanged, eventId++, FireEvent);
208+
Event_Register(&InputEvents.Down2, eventId++, FireEvent);
209+
Event_Register(&InputEvents.Up2, eventId++, FireEvent);
210+
Event_Register(&PointerEvents.Moved, eventId++, FireEvent);
211+
Event_Register(&PointerEvents.Down, eventId++, FireEvent);
212+
Event_Register(&PointerEvents.Up, eventId++, FireEvent);
213+
Event_Register(&PointerEvents.RawMoved, eventId++, FireEvent);
214+
Event_Register(&ControllerEvents.AxisUpdate, eventId++, FireEvent);
215+
Event_Register(&NetEvents.Connected, eventId++, FireEvent);
216+
Event_Register(&NetEvents.Disconnected, eventId++, FireEvent);
217+
Event_Register(&NetEvents.PluginMessageReceived, eventId++, FireEvent);
218+
}
219+
220+
static void ClassiCubeJavaLoader_Init() {
221+
cc_string initMsg = String_FromConst("ClassiCubeJavaLoader is initializing");
222+
Chat_Add(&initMsg);
223+
224+
const char* javaHomePath = getenv("JAVA_HOME");
225+
if (javaHomePath == NULL) {
226+
cc_string javaHomeNotFoundMsg = String_FromConst("&c%JAVA_HOME% variable is not defined");
227+
Chat_Add(&javaHomeNotFoundMsg);
228+
229+
return;
230+
}
231+
cc_bool jre = false;
232+
char* jvmDllPath = Concatenate(javaHomePath, JVMDLL);
233+
if (!FileExists(jvmDllPath)) {
234+
free(jvmDllPath);
235+
jre = true;
236+
jvmDllPath = Concatenate(javaHomePath, "jre\\" JVMDLL);
237+
}
238+
if (!FileExists(jvmDllPath)) {
239+
free(jvmDllPath);
240+
241+
cc_string notFoundMsg = String_FromConst("&cFailed to locate jvm.dll in %JAVA_HOME%");
242+
Chat_Add(&notFoundMsg);
243+
244+
return;
245+
}
246+
247+
if (SHOULD_LOAD_VCRUNTIME && !LoadJavaLibrary(javaHomePath, jre, VCRUNTIME)) {
248+
cc_string failedToLoadMsg = String_FromConst("&cFailed to load [\\jre]" VCRUNTIME " from %JAVA_HOME%");
249+
Chat_Add(&failedToLoadMsg);
250+
251+
return;
252+
}
253+
if (SHOULD_LOAD_MSVCP && !LoadJavaLibrary(javaHomePath, jre, MSVCP)) {
254+
cc_string failedToLoadMsg = String_FromConst("Failed to load [\\jre]" MSVCP " from %JAVA_HOME%");
255+
Chat_Add(&failedToLoadMsg);
256+
257+
return;
258+
}
259+
260+
HINSTANCE hModule = LoadLibraryA(jvmDllPath);
261+
free(jvmDllPath);
262+
263+
if (!hModule) {
264+
cc_string failedToLoadMsg = String_FromConst("&cFailed to load [\\jre]" JVMDLL " from %JAVA_HOME%");
265+
Chat_Add(&failedToLoadMsg);
266+
267+
return;
268+
}
269+
CreateJavaVM createJavaVM = (CreateJavaVM)GetProcAddress(hModule, "JNI_CreateJavaVM");
270+
271+
JavaVMInitArgs vm_args;
272+
JavaVMOption* options = malloc(sizeof(JavaVMOption) * 2);
273+
if (options == NULL) {
274+
cc_string allocationFailureMsg = String_FromConst("&cFailed to allocate memory for JavaVMOption structures");
275+
Chat_Add(&allocationFailureMsg);
276+
277+
return;
278+
}
279+
options[0].optionString = "-Djava.class.path=" JLMOD;
280+
options[1].optionString = "-Dfile.encoding=UTF8";
281+
vm_args.version = JNI_VERSION_1_8;
282+
vm_args.nOptions = 1;
283+
vm_args.options = options;
284+
vm_args.ignoreUnrecognized = false;
285+
286+
jint jvmResult = createJavaVM(&jvm, (void**)&env, &vm_args);
287+
free(options);
288+
289+
if (jvmResult != JNI_OK) {
290+
cc_string failedToStartMsg = String_FromConst("&cFailed to start JavaVM");
291+
Chat_Add(&failedToStartMsg);
292+
293+
return;
294+
}
295+
cc_string jvmOk = String_FromConst("JavaVM started");
296+
Chat_Add(&jvmOk);
297+
298+
mainClass = (*env)->FindClass(env, "ccjl/Interface");
299+
jmethodID startMethod = (*env)->GetStaticMethodID(env, mainClass, "start", "()Z");
300+
301+
jboolean bridgeResult = (*env)->CallStaticBooleanMethod(env, mainClass, startMethod);
302+
if (!bridgeResult) {
303+
cc_string failedToStartMsg = String_FromConst("&cFailed to start bridge");
304+
Chat_Add(&failedToStartMsg);
305+
306+
return;
307+
}
308+
cc_string bridgeOk = String_FromConst("Bridge started");
309+
Chat_Add(&bridgeOk);
310+
311+
SetupEvents();
312+
313+
ScheduledTask_Add(1.0 / LOADER_TICK_FREQUENCY, LoaderTick);
314+
}
315+
316+
static void ClassiCubeJavaLoader_Free() {
317+
FireEvent(SPECIAL_EVENTS_OFFSET);
318+
319+
(*jvm)->DestroyJavaVM(jvm);
320+
}
321+
322+
static void ClassiCubeJavaLoader_Reset() {
323+
FireEvent(SPECIAL_EVENTS_OFFSET + 1);
324+
}
325+
326+
static void ClassiCubeJavaLoader_OnNewMap() {
327+
FireEvent(SPECIAL_EVENTS_OFFSET + 2);
328+
}
329+
330+
static void ClassiCubeJavaLoader_OnNewMapLoaded() {
331+
FireEvent(SPECIAL_EVENTS_OFFSET + 3);
332+
}
333+
334+
EXPORT int Plugin_ApiVersion = 1;
335+
EXPORT struct IGameComponent Plugin_Component = {
336+
ClassiCubeJavaLoader_Init,
337+
ClassiCubeJavaLoader_Free,
338+
ClassiCubeJavaLoader_Reset,
339+
ClassiCubeJavaLoader_OnNewMap,
340+
ClassiCubeJavaLoader_OnNewMapLoaded
341+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.gradle
2+
build/
3+
!gradle/wrapper/gradle-wrapper.jar
4+
!**/src/main/**/build/
5+
!**/src/test/**/build/
6+
7+
### IntelliJ IDEA ###
8+
.idea/modules.xml
9+
.idea/jarRepositories.xml
10+
.idea/compiler.xml
11+
.idea/libraries/
12+
*.iws
13+
*.iml
14+
*.ipr
15+
out/
16+
!**/src/main/**/out/
17+
!**/src/test/**/out/
18+
19+
### Eclipse ###
20+
.apt_generated
21+
.classpath
22+
.factorypath
23+
.project
24+
.settings
25+
.springBeans
26+
.sts4-cache
27+
bin/
28+
!**/src/main/**/bin/
29+
!**/src/test/**/bin/
30+
31+
### NetBeans ###
32+
/nbproject/private/
33+
/nbbuild/
34+
/dist/
35+
/nbdist/
36+
/.nb-gradle/
37+
38+
### VS Code ###
39+
.vscode/
40+
41+
### Mac OS ###
42+
.DS_Store

0 commit comments

Comments
 (0)