summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphysick <mynameisgennadiy@vk.com>2026-07-28 20:29:45 +0500
committerphysick <mynameisgennadiy@vk.com>2026-07-28 20:33:20 +0500
commit7237c999918e44e2e76a6d2e2ae37ea709af08f6 (patch)
treef15f39c2ba4092bd3bf7fd7b8548c51535ee3940
parent48faa910496b1b3811c2b52f16d5a76f38ca3e48 (diff)
Added wren limiting fork support
-rw-r--r--README.md7
-rw-r--r--build.zig1
-rw-r--r--config.conf14
-rw-r--r--src/main.c4
-rw-r--r--src/puzzles/puzzle.c22
-rw-r--r--src/puzzles/puzzle.h2
-rw-r--r--src/resources/config.c100
-rw-r--r--src/resources/resources.h16
8 files changed, 157 insertions, 9 deletions
diff --git a/README.md b/README.md
index 9ffa910..86aa376 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,18 @@
# Building
## Dependencies
+This project uses the tweaked version of libwren with added timeout support.
You will have to build wren yourself. To do that, you have to clone the wren repo
in this directory with
```
-git clone https://github.com/wren-lang/wren.git
+git clone -b limited --depth=1 https://github.com/DegustatorPonos/wren.git
```
Then just build it for your OS. The build system will take care of the rest.
+I recommend using zig build system with `build.sh` or `build.bat` in
+`wren/projects/zig` dir
## Windows
For windows you have to clone the dll's in the output lib yourself
+You should also download raylib 5.5 manually.
+Put the DLLs in `raylib/lib` and header in `raylib/include/`.
diff --git a/build.zig b/build.zig
index 93ad337..9c4d736 100644
--- a/build.zig
+++ b/build.zig
@@ -27,6 +27,7 @@ pub fn build(b: *std.Build) void {
"src/utils/stringBuilder.c",
"src/wren_inter/wren_inter.c",
"src/resources/resources.c",
+ "src/resources/config.c",
"src/ui_elements/UI.c",
"src/ui_elements/TextEditor.c",
"src/scenes/sceneManager.c",
diff --git a/config.conf b/config.conf
new file mode 100644
index 0000000..fa6325e
--- /dev/null
+++ b/config.conf
@@ -0,0 +1,14 @@
+# The config for the game. All the parameters are described.
+# The comments are ignored. All the unknown parameters are ignored.
+# Unspecified parameters will be assigned to a default value.
+# Keep in mind that there is no guarantee that the game will work correctly
+# with invalid parameters.
+
+# The maximum amount of operations performed by a VM on each check.
+# Minimum value - 0 (!no code will ever be executed!)
+# Maximum value - 4294967295
+#
+# Keep in mind that setting this parameter higher will make the infinite
+# loops in your submission longer to detect. Lowering this parameter will make
+# the limitation on the code runtime stricter.
+MaxOperations=2000000
diff --git a/src/main.c b/src/main.c
index 04e857c..e83454e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,6 +5,10 @@
int main(void) {
+ if (!ReadConfig(DEFAULT_CONFIG_LOCATION)) {
+ return 1;
+ }
+
if (!InitSceneStack()) {
printf("Failed to create a scene stack\n");
return 1;
diff --git a/src/puzzles/puzzle.c b/src/puzzles/puzzle.c
index fad3f00..11328a8 100644
--- a/src/puzzles/puzzle.c
+++ b/src/puzzles/puzzle.c
@@ -1,5 +1,7 @@
+#include "../resources/resources.h"
#include "puzzle.h"
#include "../wren_inter/wren_inter.h"
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wren.h>
@@ -116,7 +118,7 @@ bool CheckResult(PuzzleIO *io, WrenVM *vm) {
}
}
-enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) {
+enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op) {
wrenEnsureSlots(vm, test->argc + 1); // finction handle + arguments
wrenGetVariable(vm, USER_MODULE_NAME, test->RecieverName, 0);
@@ -136,14 +138,15 @@ enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) {
PutIOIntoSlot(i+1, &test->argv[i], vm);
}
- WrenInterpretResult call_result = wrenCall(vm, fn_handle);
-
+ WrenInterpretResult call_result = wrenCallLim(vm, fn_handle, max_op);
switch (call_result) {
case WREN_RESULT_COMPILE_ERROR:
return RESULT_COMPTIME_ERR;
case WREN_RESULT_RUNTIME_ERROR:
return RESULT_RUNTIME_ERR;
+ case WREN_RESULT_RUNTIME_LIMIT:
+ return RESULT_TIMEOUT;
default: break;
}
@@ -160,8 +163,8 @@ enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) {
}
// Returns the res ptr. Does not modify the address
-SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *res) {
- WrenInterpretResult result = wrenInterpret(vm, "main", script);
+SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *res, size_t max_op) {
+ WrenInterpretResult result = wrenInterpretLim(vm, "main", script, max_op);
res->result = RESULT_OK;
switch (result) {
case WREN_RESULT_COMPILE_ERROR:
@@ -170,6 +173,10 @@ SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *
case WREN_RESULT_RUNTIME_ERROR:
res->result = RESULT_RUNTIME_ERR;
return res;
+ case WREN_RESULT_RUNTIME_LIMIT:
+ res->result = RESULT_TIMEOUT;
+ printf("Timeout with max_op %zu\n", max_op);
+ return res;
default: break;
}
return res;
@@ -187,6 +194,7 @@ SubmitionResult *PrepareResult(SubmitionResult *self,
}
SubmitionResult *SubmitSolution(Puzzle *self, char *script) {
+ Config *cfg = GetConfig();
if (self == NULL || script == NULL) return NULL;
SubmitionResult *res = malloc(sizeof(SubmitionResult));
@@ -196,13 +204,13 @@ SubmitionResult *SubmitSolution(Puzzle *self, char *script) {
return res;
}
- res = TryEvaluateUserCode(vm, script, res);
+ res = TryEvaluateUserCode(vm, script, res, cfg->MaxOperations);
if (res->result != RESULT_OK) {
return PrepareResult(res, vm, res->result);
}
for (size_t i = 0; i < self->TestC; i++) {
- enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm);
+ enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm, cfg->MaxOperations);
switch (test_res) {
case WREN_RESULT_COMPILE_ERROR:
return PrepareResult(res, vm, RESULT_COMPTIME_ERR);
diff --git a/src/puzzles/puzzle.h b/src/puzzles/puzzle.h
index 28ab6ca..4dfd0f8 100644
--- a/src/puzzles/puzzle.h
+++ b/src/puzzles/puzzle.h
@@ -29,7 +29,7 @@ typedef struct {
/// The user submitted code should be evaluated by this point.
/// The entry point should be registered and the absence of the
/// Main method signatire will be concidered an error
-enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm);
+enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op);
typedef struct {
char *Name;
diff --git a/src/resources/config.c b/src/resources/config.c
new file mode 100644
index 0000000..05b80cd
--- /dev/null
+++ b/src/resources/config.c
@@ -0,0 +1,100 @@
+#include "resources.h"
+#include <stdlib.h>
+#include <stddef.h>
+#include <raylib.h>
+#include <stdio.h>
+#include <string.h>
+
+Config config = {
+ .MaxOperations = DEFAULT_MAX_OPS,
+};
+
+bool startsWith(const char *str, const char *prefix) {
+ if (str == NULL || prefix == NULL) {
+ return false;
+ }
+
+ size_t prefix_len = strlen(prefix);
+
+ if (strlen(str) < prefix_len) {
+ return false;
+ }
+
+ return strncmp(str, prefix, prefix_len) == 0;
+}
+
+// Strips everything before '=' (including). Does not allocate.
+char *stripName(char *line, size_t len) {
+ if (line == NULL) {
+ return NULL;
+ }
+
+ for (size_t ptr = 0; ptr < len; ptr++) {
+ if (line[ptr] == '\0') {
+ return NULL;
+ }
+ if (line[ptr] == '=') {
+ return &line[ptr + 1];
+ }
+ }
+
+ return NULL;
+}
+
+// Reads one line and assigns the param if found
+void evaluateConfigLive(char *line) {
+ size_t len = strlen(line);
+ // Empty lines and comments
+ if (len <= 1 || line[0] == '#') return;
+ // Removing a new line
+ if (line[len-1] == '\n') {
+ line[len-1] = 0x0;
+ len -= 1;
+ }
+
+ if (startsWith(line, "MaxOperations")) {
+ char *value = stripName(line, len);
+
+ char *endptr;
+ unsigned long long parsed = strtoull(value, &endptr, 10);
+ if (value == endptr || *endptr != '\0') {
+ TraceLog(LOG_WARNING, "Failed to parse the value for MaxOperations. Provided value: '%s'", value);
+ return;
+ }
+ config.MaxOperations = parsed;
+ return;
+ }
+
+}
+
+bool ReadConfig(const char *location) {
+ FILE *file = fopen(location, "r");
+ if (file == NULL) {
+ perror("Failed to open the config file");
+ return false;
+ }
+
+ fseek(file, 0, SEEK_END);
+ long size = ftell(file);
+ rewind(file);
+
+ char* buffer = malloc(size + 1);
+ if (buffer == NULL) {
+ fclose(file);
+ perror("Failed to load the config file");
+ return false;
+ }
+
+
+ while (fgets(buffer, size, file)) {
+ evaluateConfigLive(buffer);
+ }
+
+ free(buffer);
+ fclose(file);
+ return true;
+}
+
+Config *GetConfig() {
+ return &config;
+}
diff --git a/src/resources/resources.h b/src/resources/resources.h
index d3ad840..8f6cd1d 100644
--- a/src/resources/resources.h
+++ b/src/resources/resources.h
@@ -1,8 +1,21 @@
+#include <stddef.h>
#include <raylib.h>
#ifndef RESOURCES_H
#define RESOURCES_H
+static const size_t DEFAULT_MAX_OPS = 2 * 1000 * 1000;
+static const char *DEFAULT_CONFIG_LOCATION = "config.conf";
+
+typedef struct {
+ // The maximum amount of operations performed by a VM on each check.
+ //
+ // Keep in mind that setting this parameter higher will make the infinite
+ // loops in your submission longer to detect. Lowering this parameter will make
+ // the limitation on the code runtime stricter.
+ size_t MaxOperations;
+} Config;
+
void DrawBackground();
/// #670916 as RGBA
@@ -18,6 +31,9 @@ static const Color COLORSCHEME_WHITE = { 233, 225, 185, 255 };
/// #48A56A as RGBA
static const Color COLORSCHEME_GREEN = { 72, 165, 106, 255 };
+bool ReadConfig(const char *location);
+Config *GetConfig();
+
void LoadResources();
Font *GetMainFont();
Font *GetMonoFont();