diff options
| author | physick <mynameisgennadiy@vk.com> | 2026-07-28 20:29:45 +0500 |
|---|---|---|
| committer | physick <mynameisgennadiy@vk.com> | 2026-07-28 20:33:20 +0500 |
| commit | 7237c999918e44e2e76a6d2e2ae37ea709af08f6 (patch) | |
| tree | f15f39c2ba4092bd3bf7fd7b8548c51535ee3940 | |
| parent | 48faa910496b1b3811c2b52f16d5a76f38ca3e48 (diff) | |
Added wren limiting fork support
| -rw-r--r-- | README.md | 7 | ||||
| -rw-r--r-- | build.zig | 1 | ||||
| -rw-r--r-- | config.conf | 14 | ||||
| -rw-r--r-- | src/main.c | 4 | ||||
| -rw-r--r-- | src/puzzles/puzzle.c | 22 | ||||
| -rw-r--r-- | src/puzzles/puzzle.h | 2 | ||||
| -rw-r--r-- | src/resources/config.c | 100 | ||||
| -rw-r--r-- | src/resources/resources.h | 16 |
8 files changed, 157 insertions, 9 deletions
@@ -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/`. @@ -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 @@ -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(); |
