Import from private repo

This commit is contained in:
Otto 2024-08-10 12:00:47 +03:00
commit ff7ca6cc0f
9 changed files with 536 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
target/
*.import
Cargo.lock

15
Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
godot = { git = "https://github.com/godot-rust/gdext", branch = "master" }
rand = "0.8.5"
toml = "0.8.19"

15
MiniDemoTools.gdextension Normal file
View file

@ -0,0 +1,15 @@
[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.1
reloadable = true
[libraries]
linux.debug.x86_64 = "res://addons/MiniDemoTools/librust.so"
linux.release.x86_64 = "res://addons/MiniDemoTools/librust.so"
##windows.debug.x86_64 = "res://../rust/target/debug/librust.dll"
##windows.release.x86_64 = "res://../rust/target/release/librust.dll"
[icons]
ScramblingText = "res://addons/MiniDemoTools/icons/ScrambledText.svg"

81
icons/ScrambledText.svg Normal file
View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
version="1.1"
id="svg20"
sodipodi:docname="Label.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs24">
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect3649"
is_visible="true"
lpeversion="1"
numberdashes="2"
holefactor="0"
splitsegments="true"
halfextreme="true"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect3439"
is_visible="false"
lpeversion="1"
numberdashes="2"
holefactor="0.02"
splitsegments="true"
halfextreme="true"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
<inkscape:path-effect
effect="dashed_stroke"
id="path-effect3334"
is_visible="true"
lpeversion="1"
numberdashes="2"
holefactor="0.02"
splitsegments="true"
halfextreme="true"
unifysegment="true"
message="Add &lt;b&gt;&quot;Fill Between Many LPE&quot;&lt;/b&gt; to add fill." />
</defs>
<sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="57.715148"
inkscape:cx="7.4503837"
inkscape:cy="7.7016176"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg20" />
<path
style="stroke-opacity:1;stroke-dasharray:none;stroke-width:0.6;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
d="M 5,7 A 1,1 0 0 1 5,9 1,1 0 0 1 5,7 Z"
id="path3332" />
<path
style="stroke-opacity:1;stroke-dashoffset:0;stroke-dasharray:1.8;stroke-width:1;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
d="M 6,3 A 1,1 0 0 0 5.293,3.293 l -4,4 a 1,1 0 0 0 0,1.414 l 4,4 A 1,1 0 0 0 6,13 h 8 a 1,1 0 0 0 1,-1 V 4 A 1,1 0 0 0 14,3 Z"
id="path18" />
<path
style="stroke-opacity:1;stroke-dasharray:none;stroke-width:1;stroke:#8eef97;fill-rule:nonzero;fill-opacity:1;fill:none;"
d="M 8.5606241,3 H 6 C 5.7348052,3.0000566 5.4804927,3.1054506 5.293,3.293 l -4,4 c -0.39038194,0.3904999 -0.39038194,1.0235001 0,1.414 l 4,4 C 5.4804927,12.894549 5.7348052,12.999943 6,13 h 2.5606241"
id="path3435"
sodipodi:nodetypes="cccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

17
src/MiniEditorPlugin.rs Normal file
View file

@ -0,0 +1,17 @@
#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
struct MiniEditorPlugin {
base: Base<EditorPlugin>,
}
#[godot_api]
impl IEditorPlugin for MiniEditorPlugin {
fn enter_tree(&mut self) {
}
fn exit_tree(&mut self) {
}
}

139
src/config.rs Normal file
View file

@ -0,0 +1,139 @@
use godot::prelude::*;
use godot::classes::{ConfigFile, Engine, DisplayServer, display_server::{WindowFlags, WindowMode, VSyncMode}, window::WindowInitialPosition};
#[derive(GodotClass)]
#[class(base=Node)]
struct Config {
// DISCUSSION: Should we expose these in-editor so the default values can be modified?
#[export(file = "")]
config_path: GString,
#[var]
window_height: i32,
#[var]
window_width: i32,
#[var]
is_fullscreen: bool,
#[var]
is_borderless: bool,
#[var]
monitor: i32,
#[var]
window_position_x: i32,
#[var]
window_position_y: i32,
#[var]
max_framerate: i32,
#[var]
vertical_sync: bool,
#[var]
resolution_scale: f32,
base: Base<Node>,
}
#[godot_api]
impl INode for Config {
fn init(base: Base<Node>) -> Self {
Self {
config_path: GString::from("res://config.toml"),
window_height: 600,
window_width: 400,
is_fullscreen: false,
is_borderless: true,
monitor: 0,
window_position_x: 0,
window_position_y: 0,
max_framerate: 60,
vertical_sync: true,
resolution_scale: 1.0,
base,
}
}
fn enter_tree(&mut self) {
let editor_hint = Engine::singleton().is_editor_hint();
let mut config = ConfigFile::new_gd();
let res = config.load(GString::from("res://config.toml"));
if res != godot::global::Error::OK {
godot_error!("Failed to load config file");
}
else {
//TODO This could be improved
//// GDext doesn's support defining defaults at this moment, and there's no way in hell I'm implementing checks for each config key.
//// If a config field is missing, it should throw an error, but not halt the whole demo
//
// We can get the keys and sections with get_sections() and get_section_keys() and then iterate over each one and load them if they match with the Config struct
//
self.window_width = config.get_value(GString::from("window"), GString::from("width")).to::<i32>();
self.window_height = config.get_value(GString::from("window"), GString::from("height")).to::<i32>();
self.is_fullscreen = config.get_value(GString::from("window"), GString::from("fullscreen")).to::<bool>();
self.is_borderless = config.get_value(GString::from("window"), GString::from("borderless")).to::<bool>();
self.window_position_x = config.get_value(GString::from("window"), GString::from("position_x")).to::<i32>();
self.window_position_y = config.get_value(GString::from("window"), GString::from("position_y")).to::<i32>();
self.monitor = config.get_value(GString::from("window"), GString::from("monitor")).to::<i32>();
self.max_framerate = config.get_value(GString::from("graphics"), GString::from("max_framerate")).to::<i32>();
self.vertical_sync = config.get_value(GString::from("graphics"), GString::from("vertical_sync")).to::<bool>();
self.resolution_scale = config.get_value(GString::from("graphics"), GString::from("resolution_scale")).to::<f32>();
}
if !editor_hint {
// THIS CHECK IS VERY IMPORTANT!!! I don't want to fuck my godot up again by resizing the editor and moving it out of bounds..
Self::apply_video_settings(self);
}
}
}
#[godot_api]
impl Config {
#[func]
fn apply_video_settings(&mut self) {
let mut disp_server = DisplayServer::singleton();
let mut window = self.base().get_window().unwrap();
let mut engine = Engine::singleton();
let initial_window_size = disp_server.window_get_size();
let window_offset_after_resize: Vector2i = Vector2i::from_array([(self.window_width - initial_window_size.x) / 2, (self.window_height - initial_window_size.y) / 2]);
if self.is_fullscreen {
disp_server.window_set_mode(WindowMode::FULLSCREEN)
}
else {
disp_server.window_set_mode(WindowMode::WINDOWED)
}
if self.vertical_sync {
disp_server.window_set_vsync_mode(VSyncMode::ENABLED);
}
else {
disp_server.window_set_vsync_mode(VSyncMode::DISABLED);
}
disp_server.window_set_flag(WindowFlags::BORDERLESS, self.is_borderless);
disp_server.window_set_current_screen(self.monitor);
disp_server.window_set_size(Vector2i::from_array([self.window_width, self.window_height]));
//FIXME: Doesn't seem to work on Linux, at least not on KDE+Wayland. Most likely an issue with Godot itself
// as trying to set the window position from GDscript doesn't work either...
// EDIT: It worked for a moment, then broke again... weird
window.set_initial_position(WindowInitialPosition::ABSOLUTE);
if cfg!(debug_assertions) {
godot_print!("WINDOW SIZE: {}, {}", initial_window_size.x, initial_window_size.y);
godot_print!("Initial size: {}, {}", initial_window_size.x, initial_window_size.y);
godot_print!("offset: {}, {}", window_offset_after_resize.x, window_offset_after_resize.y);
godot_print!("New pos: {}, {}", self.window_position_x - window_offset_after_resize.x, self.window_position_y - window_offset_after_resize.y);
}
window.set_position(Vector2i::from_array([self.window_position_x - window_offset_after_resize.x, initial_window_size.y - window_offset_after_resize.y]));
engine.set_max_fps(self.max_framerate);
}
}

29
src/lib.rs Normal file
View file

@ -0,0 +1,29 @@
use godot::{init::EditorRunBehavior, prelude::*};
struct MDT;
mod scrambling_text;
mod config;
mod manager;
#[gdextension]
unsafe impl ExtensionLibrary for MDT {
fn editor_run_behavior() -> EditorRunBehavior {
return EditorRunBehavior::AllClasses
}
fn min_level() -> InitLevel {
return InitLevel::Scene
}
fn on_level_init(level: InitLevel) {
match level {
InitLevel::Scene => {
if cfg!(debug_assertions) {
godot_print_rich!("[b]MiniDemoTools[/b] is in [color=red][b][u]DEBUG[/u][/b][/color] mode!")
}
}
_ => {}
}
}
}

69
src/manager.rs Normal file
View file

@ -0,0 +1,69 @@
use godot::prelude::*;
use godot::global::Key;
use godot::classes::{AudioStreamOggVorbis, Engine, InputEvent, InputEventKey};
#[derive(GodotClass)]
#[class(base=Node)]
struct Manager {
#[export(file = "*.ogg")]
bgm: GString,
#[var]
beat_count: i32,
#[var]
scenes: Array<Gd<PackedScene>>,
instanced_scenes: Vec<Gd<Node>>,
base: Base<Node>
}
#[godot_api]
impl INode for Manager {
fn init(base: Base<Node>) -> Self {
Self {
bgm: GString::from(""),
beat_count: 0,
scenes: Array::new(),
instanced_scenes: Vec::new(),
base,
}
}
fn unhandled_input(&mut self, event: Gd<InputEvent>) {
if event.is_released() {
if let Ok(key) = event.clone().try_cast::<InputEventKey>() {
if key.get_keycode() == Key::ESCAPE {
godot_print!("Goodbye!");
self.base_mut().get_tree().unwrap().quit();
}
}
}
}
}
#[godot_api]
impl Manager {
#[func]
fn set_scene(&mut self, number: i32) {
let scene = self.instanced_scenes[number as usize].clone(); // Memory leak??? Idk
self.base_mut().add_child(scene);
}
#[func]
fn register_scene(&mut self, scene: Gd<PackedScene>) {
self.instanced_scenes.push(scene.instantiate_as())
}
#[func]
fn start_audio(&mut self) {
let editor_hint = Engine::singleton().is_editor_hint();
if !editor_hint {
let bgm = self.bgm.clone();
godot_print!(" Hello world fj");
let mut stream: Gd<AudioStreamOggVorbis> = load(bgm);
stream.set_loop(false);
let mut player = AudioStreamPlayer::new_alloc();
player.set_stream(stream);
player.set_autoplay(true);
self.base_mut().add_child(player);
}
}
}

168
src/scrambling_text.rs Normal file
View file

@ -0,0 +1,168 @@
use godot::prelude::*;
use godot::classes::{Label, Engine};
#[derive(GodotClass)]
#[class(base=Label)]
struct ScramblingText {
#[export(multiline)]
chars: GString,
#[export]
scrambling: bool,
#[export]
speed: f64,
#[export]
duration: f64,
#[export]
infinite: bool,
forbidden_chars: Option<Vec<u8>>,
original_text: Option<String>,
time_since_scramble: f64, //[DOC] This is the variable used for measuring time since the letters have been scrambled
time_since_step: f64, //[DOC] This is the variable used for measuring time since the amount of static letters was changed
step_iterator: i32,
editor_hint: bool,
base: Base<Label>
}
use godot::classes::ILabel;
use rand::Rng;
#[godot_api]
impl ILabel for ScramblingText {
fn init(base: Base<Label>) -> Self {
let editor_hint = Engine::singleton().is_editor_hint();
Self {
chars: GString::from("ABCDEFGHIJKLMNOPQRSTUVWXYZ124567890".to_string()),
forbidden_chars: Some(vec![0x20, 0x0A]),
scrambling: false,
speed: 15.0,
duration: 1.0,
infinite: editor_hint,
original_text: None,
step_iterator: 0, //[DOC] This is the iterator used for changing which letters to scramble
time_since_scramble: 0.0,
time_since_step: 0.0,
editor_hint: editor_hint,
base
}
}
fn set_property(&mut self, property: StringName, value: Variant) -> bool {
// godot_print!("{:?}, {:?}", property, value);
if property == StringName::from("scrambling") {
let boolean_value = value.booleanize();
let original_text = self.original_text.clone();
if !boolean_value && !self.infinite {
Self::set_text(self, GString::from(original_text.unwrap_or("Hello World!".to_string())));
self.step_iterator = 0;
}
self.scrambling = boolean_value;
return false
}
else if property == StringName::from("infinite") {
self.step_iterator = 0;
return false
}
else {
return false
}
// Self::set_text(self, GString::from(self.original_text.clone().unwrap()))
}
fn enter_tree(&mut self) {
let text = Self::get_text(self);
self.original_text = Some(text.to_string());
}
fn process(&mut self, delta: f64) {
if self.scrambling {
let original_text = self.original_text.clone().unwrap();
let duration = self.duration;
let speed = self.speed;
// godot_print!("{}", Engine::singleton().is_editor_hint());
self.time_since_scramble += delta;
self.time_since_step += delta;
if self.time_since_scramble > duration / speed && self.time_since_step < duration {
self.time_since_scramble = 0.0;
let chars = self.chars.to_string();
let forbidden_chars = self.forbidden_chars.clone().unwrap();
let left = Self::slice_left(original_text.clone(), self.step_iterator);
let right = Self::slice_right(original_text.clone(), self.step_iterator);
// if cfg!(debug_assertions) {
// godot_print!("'{}'{}'", left.clone(), right.clone());
// }
self.base_mut().set_text(GString::from(String::from(left + &Self::get_glitch_text(right, chars, forbidden_chars))));
}
else if self.time_since_step > duration && self.step_iterator < original_text.chars().count() as i32 {
if !self.infinite | !self.editor_hint {
self.step_iterator += 1;
}
self.time_since_step = 0.0;
}
else if !self.infinite && self.step_iterator >= original_text.chars().count() as i32 {
self.step_iterator = 0;
self.scrambling = false;
}
}
}
}
#[godot_api]
impl ScramblingText {
#[func]
fn start(&mut self) {
self.scrambling = true;
}
fn get_glitch_text(text: String, chars: String, forbidden_characters: Vec<u8>) -> String {
let mut rng = rand::thread_rng();
let collect: Vec<u8> = text.bytes()
.map(|b| if !forbidden_characters.contains(&b) { chars.to_string().as_bytes()[rng.gen_range(0..chars.len())] } else { b })
.collect();
return String::from_utf8(collect).unwrap()
}
#[func]
fn set_text(&mut self, text: GString) {
self.base_mut().set_text(text)
}
#[func]
fn get_text(&mut self) -> GString {
return self.base_mut().get_text()
}
fn slice_left(text: String, amount: i32) -> String {
// let len: i32 = text.chars().count() as i32;
let mut res: String = String::new();
for i in 0..amount {
res.push(text.chars().nth(i as usize).unwrap());
}
res
}
fn slice_right(text: String, amount: i32) -> String {
let res = text.clone().split_off(amount as usize);
return res
}
}