This commit is contained in:
2026-09-10 07:47:15 -04:00
parent 1f555ed3e4
commit 1c4d0ecbfe
39 changed files with 1499 additions and 2357 deletions
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
./scavgame.x86_64 &
GODOT_PID=$!
wmctrl -r "scavgame" -b add,hidden
wait "$GODOT_PID"
-1243
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://bj1b5wm8lhd0v"
uid="uid://b35ewh20gulwl"
path="res://.godot/imported/scavgame.144x144.png-b48426c1c0d44c524725caf94371d8cf.ctex"
metadata={
"vram_texture": false
+1 -1
View File
@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://c3ffnf40pfswc"
uid="uid://dxxnbj0y02str"
path="res://.godot/imported/scavgame.180x180.png-98da9fc07cc8cd6f3596b8214cb44abf.ctex"
metadata={
"vram_texture": false
+1 -1
View File
@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://4ele4xirpeuo"
uid="uid://cwtujh1x71cxy"
path="res://.godot/imported/scavgame.512x512.png-fc91a6f75062f7a09d36054b561598b5.ctex"
metadata={
"vram_texture": false
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c08c15fn25u5a"
path="res://.godot/imported/scavgame.apple-touch-icon.png-fae91e97aebbd193ef8488b9ce4380d1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Build/scavgame.apple-touch-icon.png"
dest_files=["res://.godot/imported/scavgame.apple-touch-icon.png-fae91e97aebbd193ef8488b9ce4380d1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.
+1 -1
View File
@@ -110,7 +110,7 @@ body {
<script src="scavgame.js"></script>
<script>
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"emscriptenPoolSize":8,"ensureCrossOriginIsolationHeaders":true,"executable":"scavgame","experimentalVK":false,"fileSizes":{"scavgame.pck":200656,"scavgame.wasm":1601069},"focusCanvas":true,"gdextensionLibs":[],"godotPoolSize":4,"serviceWorker":"scavgame.service.worker.js"};
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"emscriptenPoolSize":8,"ensureCrossOriginIsolationHeaders":true,"executable":"scavgame","experimentalVK":false,"fileSizes":{"scavgame.pck":232728,"scavgame.wasm":1601069},"focusCanvas":true,"gdextensionLibs":[],"godotPoolSize":4,"serviceWorker":"scavgame.service.worker.js"};
const GODOT_THREADS_ENABLED = true;
const engine = new Engine(GODOT_CONFIG);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

-40
View File
@@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dd0gt71ad26cw"
path="res://.godot/imported/scavgame.icon.png-73f49704b23c124153b229d5b0cda394.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Build/scavgame.icon.png"
dest_files=["res://.godot/imported/scavgame.icon.png-73f49704b23c124153b229d5b0cda394.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file not shown.
+1 -1
View File
@@ -2,7 +2,7 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://d2j704bew40yv"
uid="uid://cgr0gin458n38"
path="res://.godot/imported/scavgame.png-7a036069753eca4aeee3fbdd0ed99f6b.ctex"
metadata={
"vram_texture": false
+1 -1
View File
@@ -4,7 +4,7 @@
// Incrementing CACHE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
/** @type {string} */
const CACHE_VERSION = '1788537868|6220518696';
const CACHE_VERSION = '1788972329|18914709609';
/** @type {string} */
const CACHE_PREFIX = 'scavgame-sw-cache-';
const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION;
-906
View File
@@ -1,906 +0,0 @@
#!/usr/bin/env python3
import asyncio
import json
import os
import uuid
from pathlib import Path
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from websockets.asyncio.server import serve
from websockets.exceptions import ConnectionClosed
# ============================================================
# Configuration
# ============================================================
HTTP_HOST = "0.0.0.0"
HTTP_PORT = 8080
WS_HOST = "0.0.0.0"
WS_PORT = 8081
GAME_EXECUTABLE = Path(
"/data/1/Projects/Scavenger-2027/Build/scavgame.x86_64"
)
GAME_DIRECTORY = GAME_EXECUTABLE.parent
WEB_DIRECTORY = GAME_DIRECTORY
MAX_SESSIONS = 4
DEBUG_LOGGING = True
sessions: dict[str, "GameSession"] = {}
# ============================================================
# Logging
# ============================================================
def log(*args):
print("[Server]", *args, flush=True)
def debug(*args):
if DEBUG_LOGGING:
print("[Server][DEBUG]", *args, flush=True)
def warning(*args):
print("[Server][WARNING]", *args, flush=True)
def error(*args):
print("[Server][ERROR]", *args, flush=True)
# ============================================================
# Message helpers
# ============================================================
def normalize_sdp_message(message: dict) -> dict | None:
"""
Normalizes these formats:
Browser:
{
"type": "offer",
"offer": {
"type": "offer",
"sdp": "..."
}
}
Godot:
{
"type": "offer",
"sdp": "..."
}
The same applies to answers.
"""
message_type = message.get("type")
if message_type not in {"offer", "answer"}:
return None
nested_key = message_type
nested = message.get(nested_key)
if isinstance(nested, dict):
sdp = nested.get("sdp", "")
if not sdp:
return None
return {
"type": message_type,
"sdp": sdp
}
sdp = message.get("sdp", "")
if not sdp:
return None
return {
"type": message_type,
"sdp": sdp
}
def normalize_ice_message(message: dict) -> dict | None:
"""
Normalizes both:
{ "type": "ice", "candidate": {...} }
and:
{ "type": "candidate", "candidate": {...} }
"""
candidate = message.get("candidate")
if not isinstance(candidate, dict):
return None
if not candidate.get("candidate"):
return None
return {
"type": "ice",
"candidate": candidate
}
# ============================================================
# Game session
# ============================================================
class GameSession:
def __init__(
self,
browser,
requested_session_id: str | None = None
):
self.id = (
requested_session_id
if requested_session_id
else uuid.uuid4().hex
)
self.browser = browser
self.godot = None
self.process = None
self.browser_connected = True
self.godot_connected = False
self.stopping = False
self.send_lock = asyncio.Lock()
# --------------------------------------------------------
# Game process
# --------------------------------------------------------
async def start(self):
if not GAME_EXECUTABLE.exists():
raise FileNotFoundError(
f"Game executable not found: {GAME_EXECUTABLE}"
)
if not os.access(GAME_EXECUTABLE, os.X_OK):
raise PermissionError(
f"Game is not executable: {GAME_EXECUTABLE}"
)
env = os.environ.copy()
env["SCAV_SESSION_ID"] = self.id
# Keep Forward+ rendering.
env["DISPLAY"] = ":1"
# Start minimized and avoid taking focus.
env["SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS"] = "1"
command = [
str(GAME_EXECUTABLE),
"--session-id",
self.id,
"--debug-logging",
"--resolution",
"1x1",
]
debug(
f"[{self.id}] Starting game command:",
command
)
self.process = await asyncio.create_subprocess_exec(
*command,
cwd=str(GAME_DIRECTORY),
env=env,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
)
log(
f"[{self.id}] Started game "
f"PID={self.process.pid}"
)
asyncio.create_task(
self.read_output()
)
async def read_output(self):
process = self.process
if process is None:
return
if process.stdout is None:
return
try:
async for line in process.stdout:
output = line.decode(
errors="replace"
).rstrip()
print(
f"[{self.id}] {output}",
flush=True
)
except asyncio.CancelledError:
raise
except Exception as exc:
error(
f"[{self.id}] Output reader error:",
exc
)
async def stop(self):
if self.stopping:
return
self.stopping = True
process = self.process
self.process = None
if process is None:
return
if process.returncode is not None:
return
log(
f"[{self.id}] Stopping game "
f"PID={process.pid}"
)
try:
process.terminate()
await asyncio.wait_for(
process.wait(),
timeout=3
)
log(
f"[{self.id}] Game exited "
f"code={process.returncode}"
)
except asyncio.TimeoutError:
warning(
f"[{self.id}] Game did not exit; "
f"killing PID={process.pid}"
)
try:
process.kill()
await process.wait()
except ProcessLookupError:
pass
except ProcessLookupError:
pass
# --------------------------------------------------------
# WebSocket sending
# --------------------------------------------------------
async def send_json(
self,
websocket,
message: dict
) -> bool:
if websocket is None:
return False
try:
payload = json.dumps(message)
async with self.send_lock:
await websocket.send(payload)
debug(
f"[{self.id}] Sent:",
message
)
return True
except ConnectionClosed:
return False
except Exception as exc:
error(
f"[{self.id}] Send error:",
exc
)
return False
async def send_to_godot(
self,
message: dict
) -> bool:
if self.godot is None:
warning(
f"[{self.id}] Cannot send to Godot; "
f"Godot is not connected"
)
return False
success = await self.send_json(
self.godot,
message
)
if not success:
self.godot = None
self.godot_connected = False
return success
async def send_to_browser(
self,
message: dict
) -> bool:
if self.browser is None:
warning(
f"[{self.id}] Cannot send to browser; "
f"browser is not connected"
)
return False
success = await self.send_json(
self.browser,
message
)
if not success:
self.browser = None
self.browser_connected = False
return success
# ============================================================
# HTTP server
# ============================================================
class HTTPHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(
*args,
directory=str(WEB_DIRECTORY),
**kwargs
)
def log_message(self, format_string, *args):
print(
"[HTTP] " + format_string % args,
flush=True
)
def start_http_server():
server = ThreadingHTTPServer(
(HTTP_HOST, HTTP_PORT),
HTTPHandler
)
log(
f"HTTP server listening on "
f"http://{HTTP_HOST}:{HTTP_PORT}"
)
try:
server.serve_forever()
finally:
server.server_close()
# ============================================================
# Browser connection
# ============================================================
async def handle_browser(
websocket,
requested_session_id: str | None = None
):
if len(sessions) >= MAX_SESSIONS:
await websocket.send(json.dumps({
"type": "error",
"message": "Server is full"
}))
await websocket.close()
return
session = GameSession(
websocket,
requested_session_id
)
if session.id in sessions:
await websocket.send(json.dumps({
"type": "error",
"message": "Session ID is already in use"
}))
await websocket.close()
return
sessions[session.id] = session
log(
f"[{session.id}] Browser connected"
)
try:
await session.send_to_browser({
"type": "session_created",
"session_id": session.id
})
await session.start()
# Wait for Godot to connect.
for _ in range(100):
if session.godot is not None:
break
await asyncio.sleep(0.1)
if session.godot is None:
warning(
f"[{session.id}] Godot did not connect"
)
await session.send_to_browser({
"type": "error",
"message": "Godot did not connect"
})
return
await session.send_to_godot({
"type": "browser_connected"
})
await session.send_to_browser({
"type": "godot_connected"
})
log(
f"[{session.id}] Browser and Godot paired"
)
async for raw in websocket:
try:
message = json.loads(raw)
except json.JSONDecodeError:
warning(
f"[{session.id}] Invalid browser JSON:",
raw
)
continue
if not isinstance(message, dict):
warning(
f"[{session.id}] Browser message "
f"is not an object"
)
continue
debug(
f"[{session.id}] Browser message:",
message
)
message_type = message.get("type")
# ------------------------------------------------
# WebRTC offer
# ------------------------------------------------
if message_type == "offer":
normalized = normalize_sdp_message(
message
)
if normalized is None:
warning(
f"[{session.id}] Invalid browser offer"
)
continue
await session.send_to_godot(
normalized
)
# ------------------------------------------------
# WebRTC ICE
# ------------------------------------------------
elif message_type in {
"ice",
"candidate"
}:
normalized = normalize_ice_message(
message
)
if normalized is None:
warning(
f"[{session.id}] Invalid browser ICE"
)
continue
await session.send_to_godot(
normalized
)
else:
debug(
f"[{session.id}] Ignoring browser message:",
message_type
)
except ConnectionClosed:
log(
f"[{session.id}] Browser WebSocket closed"
)
except Exception as exc:
error(
f"[{session.id}] Browser error:",
exc
)
finally:
log(
f"[{session.id}] Browser disconnected"
)
session.browser_connected = False
if session.godot is not None:
await session.send_to_godot({
"type": "browser_disconnected"
})
await asyncio.sleep(0.2)
try:
await session.godot.close()
except Exception:
pass
session.godot = None
session.godot_connected = False
await session.stop()
sessions.pop(
session.id,
None
)
log(
f"[{session.id}] Session removed"
)
# ============================================================
# Godot connection
# ============================================================
async def handle_godot(
websocket,
session_id: str
):
session = sessions.get(session_id)
if session is None:
warning(
f"[{session_id}] Godot attempted to connect "
f"to unknown session"
)
await websocket.send(json.dumps({
"type": "error",
"message": "Unknown session"
}))
await websocket.close()
return
if session.godot is not None:
warning(
f"[{session.id}] Replacing existing Godot connection"
)
try:
await session.godot.close()
except Exception:
pass
session.godot = websocket
session.godot_connected = True
log(
f"[{session.id}] Godot connected"
)
try:
await session.send_to_godot({
"type": "registered",
"session_id": session.id
})
if session.browser_connected:
await session.send_to_godot({
"type": "browser_connected"
})
await session.send_to_browser({
"type": "godot_connected"
})
async for raw in websocket:
try:
message = json.loads(raw)
except json.JSONDecodeError:
warning(
f"[{session.id}] Invalid Godot JSON:",
raw
)
continue
if not isinstance(message, dict):
warning(
f"[{session.id}] Godot message "
f"is not an object"
)
continue
debug(
f"[{session.id}] Godot message:",
message
)
message_type = message.get("type")
# ------------------------------------------------
# WebRTC answer
# ------------------------------------------------
if message_type == "answer":
normalized = normalize_sdp_message(
message
)
if normalized is None:
warning(
f"[{session.id}] Invalid Godot answer"
)
continue
await session.send_to_browser({
"type": "answer",
"answer": {
"type": "answer",
"sdp": normalized["sdp"]
}
})
# ------------------------------------------------
# WebRTC ICE
# ------------------------------------------------
elif message_type in {
"ice",
"candidate"
}:
normalized = normalize_ice_message(
message
)
if normalized is None:
warning(
f"[{session.id}] Invalid Godot ICE"
)
continue
await session.send_to_browser(
normalized
)
else:
debug(
f"[{session.id}] Ignoring Godot message:",
message_type
)
except ConnectionClosed:
log(
f"[{session.id}] Godot WebSocket closed"
)
except Exception as exc:
error(
f"[{session.id}] Godot error:",
exc
)
finally:
log(
f"[{session.id}] Godot disconnected"
)
if session.godot is websocket:
session.godot = None
session.godot_connected = False
if session.browser is not None:
await session.send_to_browser({
"type": "godot_disconnected"
})
# ============================================================
# WebSocket routing
# ============================================================
async def websocket_handler(websocket):
try:
raw = await websocket.recv()
message = json.loads(raw)
if not isinstance(message, dict):
await websocket.close()
return
role = message.get("role")
debug(
"Initial WebSocket registration:",
message
)
if role == "browser":
requested_session_id = message.get(
"session_id"
)
await handle_browser(
websocket,
requested_session_id
)
elif role == "godot":
session_id = message.get(
"session_id"
)
if not session_id:
await websocket.send(json.dumps({
"type": "error",
"message": "Missing session_id"
}))
await websocket.close()
return
await handle_godot(
websocket,
session_id
)
else:
await websocket.send(json.dumps({
"type": "error",
"message": "Invalid role"
}))
await websocket.close()
except ConnectionClosed:
pass
except json.JSONDecodeError:
warning(
"Received invalid initial WebSocket JSON"
)
try:
await websocket.close()
except Exception:
pass
except Exception as exc:
error(
"WebSocket handler error:",
exc
)
# ============================================================
# WebSocket server
# ============================================================
async def websocket_server():
log(
f"WebSocket server listening on "
f"ws://{WS_HOST}:{WS_PORT}"
)
async with serve(
websocket_handler,
WS_HOST,
WS_PORT
):
await asyncio.Future()
# ============================================================
# Main
# ============================================================
async def main():
http_task = asyncio.to_thread(
start_http_server
)
websocket_task = asyncio.create_task(
websocket_server()
)
await asyncio.gather(
http_task,
websocket_task
)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print(
"\nServer stopped",
flush=True
)
-1
View File
@@ -1 +0,0 @@
4.6.2.stable
+130 -66
View File
@@ -1,97 +1,161 @@
extends CharacterBody3D
const SPEED := 5.0
const JUMP_VELOCITY := 4.5
const MOUSE_SENSITIVITY := 0.003
# Acceleration and deceleration are m/s².
# max_speed is m/s.
@export var accel := 6.7
@export var deccel := 8.5
@export var max_speed := 6.2
const ACCEL := 20.0
const DECEL := 15.0
@export var has_suit := true
@export var has_hvms2 := true
@export var gravity := 9.8
@export var jump_velocity := 4.5
# Mouse sensitivity
@export var mouse_sensitivity := 0.003
# Maximum camera pitch in degrees
@export var max_pitch := 89.0
var movement_vec := Vector3.ZERO
var input_vec := Vector4.ZERO
var jump := false
var air_jumps := 0
func _ready() -> void:
if not has_suit:
$yaw/pitch/Camera/Hud.visible = false
if has_hvms2:
max_speed = 30.0
accel = 10
deccel = 20
jump_velocity = 10
# Capture the mouse when the game starts
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _input(event: InputEvent) -> void:
if event is InputEventKey:
if event.keycode == KEY_ESCAPE and event.pressed:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
return
# Mouse-look
if event is InputEventMouseMotion:
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
# Horizontal rotation: rotate the player
$yaw.rotate_y(-event.relative.x * mouse_sensitivity)
if event.keycode == KEY_SPACE and event.pressed and is_on_floor():
velocity.y = JUMP_VELOCITY
if event.keycode == KEY_E and event.pressed and not event.echo:
var node = $yaw/pitch/Facing.get_collider()
if node and node.has_method("interact"):
node.interact(self)
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
$yaw.rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
$yaw/pitch.rotate_x(-event.relative.y * MOUSE_SENSITIVITY)
# Vertical rotation: rotate the camera
$yaw/pitch.rotate_x(-event.relative.y * mouse_sensitivity)
# Clamp pitch so the camera cannot flip upside down
$yaw/pitch.rotation.x = clamp(
$yaw/pitch.rotation.x,
deg_to_rad(-89.0),
deg_to_rad(89.0)
deg_to_rad(-max_pitch),
deg_to_rad(max_pitch)
)
return
# Mouse click captures the mouse again
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
return
# Keyboard input
if event is InputEventKey:
var pressed: bool = event.pressed
match event.keycode:
KEY_W:
input_vec.w = 1.0 if pressed else 0.0
KEY_S:
input_vec.x = 1.0 if pressed else 0.0
KEY_A:
input_vec.y = 1.0 if pressed else 0.0
KEY_D:
input_vec.z = 1.0 if pressed else 0.0
KEY_SPACE:
if pressed and is_on_floor():
jump=true
KEY_ESCAPE:
if pressed:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
KEY_SHIFT:
if has_hvms2:
velocity.y = max(velocity.y, -3) if pressed else velocity.y
gravity = 3 if pressed else 9.8
if is_on_floor():
velocity=$yaw/pitch/Camera.basis*Vector3(0,0,50)
func _physics_process(delta: float) -> void:
# Gravity
if not is_on_floor():
velocity += get_gravity() * delta
# W/S/A/D input
var input_direction := Vector2(
input_vec.z - input_vec.y,
input_vec.w - input_vec.x
)
# Read keyboard state directly
var input_x := 0.0
var input_z := 0.0
if Input.is_key_pressed(KEY_A):
input_x -= 1.0
if Input.is_key_pressed(KEY_D):
input_x += 1.0
if Input.is_key_pressed(KEY_W):
input_z -= 1.0
if Input.is_key_pressed(KEY_S):
input_z += 1.0
# Create movement vector
var input_direction := Vector3(input_x, 0.0, input_z)
# Prevent diagonal movement from being faster
if input_direction.length_squared() > 0.0:
if input_direction.length() > 1.0:
input_direction = input_direction.normalized()
# Rotate movement relative to yaw
var direction : Vector3 = $yaw.global_transform.basis * input_direction
direction.y = 0.0
# Camera yaw only
var yaw_basis: Basis = $yaw.global_transform.basis
if direction.length_squared() > 0.0:
direction = direction.normalized()
var forward := -yaw_basis.z
var right := yaw_basis.x
# Target velocity
var target := direction * SPEED
forward.y = 0.0
right.y = 0.0
var acceleration := ACCEL
if direction.length_squared() == 0.0:
acceleration = DECEL
forward = forward.normalized()
right = right.normalized()
# Smooth movement toward target
velocity.x = move_toward(
var target_direction := (
right * input_direction.x
+ forward * input_direction.y
)
# Target horizontal velocity in m/s
var target_velocity := target_direction * max_speed
# Current horizontal velocity in m/s
var current_velocity := Vector3(
velocity.x,
target.x,
0.0,
velocity.z
)
# Acceleration/deceleration in m/s²
var acceleration := accel if target_direction != Vector3.ZERO else deccel
current_velocity = current_velocity.move_toward(
target_velocity,
acceleration * delta
)
velocity.z = move_toward(
velocity.z,
target.z,
acceleration * delta
)
velocity.x = current_velocity.x
velocity.z = current_velocity.z
# Gravity
if not is_on_floor():
velocity.y -= gravity * delta
else:
if jump:
velocity.y=jump_velocity
jump=false
else:
velocity.y=0.0
if global_position.y<=-1000:
get_tree().reload_current_scene()
move_and_slide()
+4 -1
View File
@@ -1,4 +1,7 @@
extends Node2D
func _ready() -> void:
$Container.size=get_viewport_rect().size
func set_tooltip(text: String) -> void:
$Tooltip.text=text
$Container/Tooltip.text=text
Binary file not shown.
Binary file not shown.
+72
View File
@@ -0,0 +1,72 @@
extends MeshInstance3D
@export var max_lights := 256
@export var camera: Camera3D
var lights: Array[Light3D] = []
func _ready() -> void:
find_lights(get_tree().current_scene)
func find_lights(node: Node) -> void:
if node is Light3D:
lights.append(node)
for child in node.get_children():
find_lights(child)
func _process(_delta: float) -> void:
var mat := material_override as ShaderMaterial
if mat == null:
return
if camera == null:
camera = get_viewport().get_camera_3d()
if camera == null:
return
mat.set_shader_parameter(
"inv_projection",
camera.get_camera_projection().inverse()
)
mat.set_shader_parameter(
"inv_view",
camera.global_transform
)
if mat == null:
return
var positions := PackedVector3Array()
var colors := PackedColorArray()
var energies := PackedFloat32Array()
var ranges := PackedFloat32Array()
for light in lights:
if not is_instance_valid(light):
continue
if not light.visible:
continue
positions.append(light.global_position)
colors.append(light.light_color)
energies.append(light.light_energy)
if light is OmniLight3D:
ranges.append(light.omni_range)
elif light is SpotLight3D:
ranges.append(light.spot_range)
else:
ranges.append(100.0)
if positions.size() >= max_lights:
break
mat.set_shader_parameter("light_positions", positions)
mat.set_shader_parameter("light_colors", colors)
mat.set_shader_parameter("light_energies", energies)
mat.set_shader_parameter("light_ranges", ranges)
mat.set_shader_parameter("light_count", positions.size())
+1
View File
@@ -0,0 +1 @@
uid://blvu4cfl61262
+135
View File
@@ -0,0 +1,135 @@
shader_type spatial;
render_mode unshaded, fog_disabled, depth_draw_never, cull_disabled, blend_mix;
const int MAX_LIGHTS = 256;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear;
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
// Camera matrices — set from GDScript.
uniform mat4 inv_projection;
uniform mat4 inv_view;
// Fog
uniform vec3 fog_color : source_color = vec3(0.55, 0.65, 0.75);
uniform float fog_density : hint_range(0.0, 10.0) = 0.5;
uniform float fog_height_falloff : hint_range(0.0, 10.0) = 0.15;
// Light scattering
uniform int light_count = 0;
uniform vec3 light_positions[MAX_LIGHTS];
uniform vec4 light_colors[MAX_LIGHTS];
uniform float light_energies[MAX_LIGHTS];
uniform float light_ranges[MAX_LIGHTS];
uniform float scattering : hint_range(0.0, 2.0) = 0.5;
uniform float scattering_power : hint_range(0.1, 32.0) = 8.0;
uniform int steps : hint_range(8, 64) = 32;
vec3 reconstruct_view_position(vec2 uv, float depth) {
vec3 ndc = vec3(uv * 2.0 - 1.0, depth);
vec4 view_pos = inv_projection * vec4(ndc, 1.0);
view_pos.xyz /= view_pos.w;
return view_pos.xyz;
}
void fragment() {
vec2 screen_uv = SCREEN_UV;
vec4 scene = texture(screen_texture, screen_uv);
float depth = texture(depth_texture, screen_uv).r;
vec3 ray_end_view =
reconstruct_view_position(screen_uv, depth);
vec3 camera_position =
(inv_view * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
vec3 ray_end =
(inv_view * vec4(ray_end_view, 1.0)).xyz;
vec3 ray_vector = ray_end - camera_position;
float ray_length = length(ray_vector);
vec3 ray_dir = normalize(ray_vector);
float step_length = ray_length / float(steps);
vec3 accumulated = vec3(0.0);
float transmittance = 1.0;
for (int i = 0; i < steps; i++) {
float t = (float(i) + 0.5) / float(steps);
vec3 world_position =
camera_position +
ray_dir * (ray_length * t);
float height_density =
exp(-world_position.y * fog_height_falloff);
float density =
fog_density * height_density;
vec3 light_scattering = vec3(0.0);
for (int l = 0; l < MAX_LIGHTS; l++) {
if (l >= light_count)
break;
vec3 to_light =
light_positions[l] - world_position;
float light_distance = length(to_light);
if (light_distance <= 0.001)
continue;
vec3 light_dir =
to_light / light_distance;
float attenuation =
1.0 - smoothstep(
0.0,
light_ranges[l],
light_distance
);
attenuation /=
1.0 + light_distance * light_distance * 0.01;
float phase =
pow(
max(dot(-ray_dir, light_dir), 0.0),
scattering_power
);
light_scattering +=
light_colors[l].rgb *
light_energies[l] *
attenuation *
phase *
scattering;
}
float absorption =
exp(-density * step_length * 10.0);
accumulated +=
(fog_color + light_scattering) *
(1.0 - absorption) *
transmittance;
transmittance *= absorption;
}
ALBEDO =
scene.rgb * transmittance +
accumulated;
ALPHA = 1.0;
}
-47
View File
@@ -1,47 +0,0 @@
shader_type spatial;
render_mode blend_add, cull_disabled, unshaded;
uniform sampler2D dust : source_color;
uniform float dust_speed = 0.03;
uniform vec4 color : source_color;
uniform float intensity = 2.75;
uniform float softness = 3.0;
// Godot 4 explicit texture hints
uniform sampler2D depth_texture : hint_depth_texture, filter_linear_mipmap;
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
uniform float proximity_fade_distance = 0.5;
uniform float distance_fade_min = 1.0;
uniform float distance_fade_max = 2.0;
varying vec3 obj_vertex;
void vertex() {
obj_vertex = VERTEX;
}
void fragment() {
// Dust texture sampling using screen coordinates
vec2 dust_uv = vec2(SCREEN_UV.x + dust_speed * TIME, SCREEN_UV.y);
vec4 dust_overlay = texture(dust, dust_uv);
float attenuation = clamp(1.0 + obj_vertex.y / intensity, 0.0, 1.0);
float soft_edge = clamp(pow(dot(NORMAL, normalize(VERTEX)), softness), 0.0, 1.0);
ALBEDO = dust_overlay.rgb * color.rgb * attenuation * soft_edge;
// Proximity Fade calculation in Godot 4
float depth_raw = texture(depth_texture, SCREEN_UV).r;
vec4 ndc = vec4(SCREEN_UV * 2.0 - 1.0, depth_raw, 1.0);
vec4 view_pos = INV_PROJECTION_MATRIX * ndc;
view_pos.xyz /= view_pos.w;
float depth_diff = -VERTEX.z - (-view_pos.z);
float proximity_alpha = clamp(depth_diff / proximity_fade_distance, 0.0, 1.0);
// Distance Fade calculation
float dist_alpha = clamp(smoothstep(distance_fade_min, distance_fade_max, -VERTEX.z), 0.0, 1.0);
ALPHA = proximity_alpha * dist_alpha;
}
BIN
View File
Binary file not shown.
Binary file not shown.
+805
View File
@@ -0,0 +1,805 @@
extends Node
const CONSOLE_THEME : String = &"console/theme"
const CONSOLE_SCALE : String = &"console/scale"
const CONSOLE_HEIGHT : String = &"console/height"
const CONSOLE_COLOR_WARNING : String = &"console/color_warning"
const CONSOLE_COLOR_ERROR : String = &"console/color_error"
const CONSOLE_COLOR_INFO : String = &"console/color_info"
const CONSOLE_COLOR_LITERAL : String = &"console/color_literal"
const CONSOLE_TABSTOP : String = &"console/tabstop"
const color_dictionary : Dictionary[String, Color] = {
CONSOLE_COLOR_ERROR: Color.LIGHT_CORAL,
CONSOLE_COLOR_INFO: Color.LIGHT_BLUE,
CONSOLE_COLOR_LITERAL: Color.PALE_GREEN,
CONSOLE_COLOR_WARNING: Color.LIGHT_GOLDENROD
}
var enabled : bool = true
var enable_on_release_build : bool = false : set = set_enable_on_release_build
var pause_enabled : bool = false
var font_size : int : set = _set_font_size
## What visual scale should the console be
var console_scale : float : set = set_console_scale
## Is the console in full screen or part screen mode
var console_full_screen : bool = false
var _conosle_tween_time : float = 5
signal console_opened
signal console_closed
signal console_unknown_command
signal console_cvar_changed(cvar_name : String, value : Variant)
class ConsoleCommand:
var function : Callable
var arguments : PackedStringArray
var required : int
var description : String
var hidden : bool
func _init(in_function : Callable, in_arguments : PackedStringArray, in_required : int = 0, in_description : String = ""):
function = in_function
arguments = in_arguments
required = in_required
description = in_description
class ConsoleCvar:
var name : String
var description : String
var type : int
var save : bool
var value : Variant
var object : Object
var property : String
func _init(in_name : String, in_type : int, in_description : String = "", in_save : bool = false):
name = in_name
type = in_type
description = in_description
save = in_save
func is_reference() -> bool:
return object != null
func is_alive() -> bool:
return object == null or is_instance_valid(object)
func get_value() -> Variant:
if object != null:
return object.get_indexed(property)
return value
func set_value(new_value : Variant) -> void:
if object != null:
object.set_indexed(property, new_value)
else:
value = new_value
var theme : Theme
var canvas_layer : CanvasLayer = CanvasLayer.new()
var v_box_container : VBoxContainer = VBoxContainer.new()
# If you want to customize the way the console looks, you can direcly modify
# the properties of the rich text and line edit here:
var rich_label : RichTextLabel = RichTextLabel.new()
var panel : Panel = Panel.new()
var line_edit : LineEdit = LineEdit.new()
var console_commands : Dictionary[String, ConsoleCommand] = {}
var console_cvars : Dictionary[String, ConsoleCvar] = {}
# Pending values are applied when cvars are registered (if loaded from config)
var _pending_cvar_values : Dictionary[String, Variant] = {}
var command_parameters : Dictionary[String, PackedStringArray] = {}
var console_history : Array[String] = []
var console_history_index : int = 0
var was_paused_already : bool = false
var tab_string : String = " "
var text_block_cache : Array[String]
## Usage: Console.add_command("command_name", <function to call>, <number of arguments or array of argument names>, <required number of arguments>, "Help description")
func add_command(command_name : String, function : Callable, arguments = [], required: int = 0, description : String = "") -> void:
if (arguments is int):
# Legacy call using an argument number
var param_array : PackedStringArray
for i in range(arguments):
param_array.append("arg_" + str(i + 1))
console_commands[command_name] = ConsoleCommand.new(function, param_array, required, description)
elif (arguments is Array):
# New array argument system
var str_args : PackedStringArray
for argument in arguments:
str_args.append(str(argument))
console_commands[command_name] = ConsoleCommand.new(function, str_args, required, description)
## Adds a secret command that will not show up in the help or auto-complete.
func add_hidden_command(command_name : String, function : Callable, arguments = [], required : int = 0) -> void:
add_command(command_name, function, arguments, required)
console_commands[command_name].hidden = true
## Removes a command from the console. This should be called on a script's _exit_tree()
## if you have console commands for things that are unloaded before the project closes.
func remove_command(command_name : String) -> void:
console_commands.erase(command_name)
command_parameters.erase(command_name)
## Registers an auto-managed cvar that stores its value internally.
## Type the name to print the value, or "name value" to set it. The value is coerced to the
## type of default_value. If save is true, the value is persisted to user://console_cvars.txt.
## Usage: Console.add_cvar("cl_fov", 90.0, "Field of view.", true)
func add_cvar(cvar_name : String, default_value : Variant, description : String = "", save : bool = false) -> void:
var cvar := ConsoleCvar.new(cvar_name, typeof(default_value), description, save)
cvar.value = default_value
console_cvars[cvar_name] = cvar
_apply_pending_cvar_value(cvar)
## Registers a cvar that reads from and writes to a property on another object.
## The cvar type is inferred from the property's current value. property may be a nested path
## (ex: "position:x").
## Usage: Console.add_cvar_reference("cl_fov", camera, "fov", "Camera field of view.")
func add_cvar_reference(cvar_name : String, object : Object, property : String, description : String = "", save : bool = false) -> void:
if not is_instance_valid(object):
print_error("Cannot register cvar \"%s\": invalid object." % cvar_name)
return
var cvar := ConsoleCvar.new(cvar_name, typeof(object.get_indexed(property)), description, save)
cvar.object = object
cvar.property = property
console_cvars[cvar_name] = cvar
_apply_pending_cvar_value(cvar)
## Removes a cvar. Call this on _exit_tree() for reference cvars whose object is freed early.
func remove_cvar(cvar_name : String) -> void:
console_cvars.erase(cvar_name)
## Returns the current value of a cvar, or null if it doesn't exist / its object was freed.
func get_cvar(cvar_name : String) -> Variant:
if console_cvars.has(cvar_name):
var cvar : ConsoleCvar = console_cvars[cvar_name]
if cvar.is_alive():
return cvar.get_value()
return null
## Sets the value of a cvar programmatically (no string coercion) and emits console_cvar_changed.
func set_cvar(cvar_name : String, value : Variant) -> void:
if console_cvars.has(cvar_name):
var cvar : ConsoleCvar = console_cvars[cvar_name]
if cvar.is_alive():
cvar.set_value(value)
console_cvar_changed.emit(cvar_name, value)
func _apply_pending_cvar_value(cvar : ConsoleCvar) -> void:
if cvar.save and _pending_cvar_values.has(cvar.name):
var saved_value : Variant = _pending_cvar_values[cvar.name]
if typeof(saved_value) != cvar.type:
saved_value = type_convert(saved_value, cvar.type)
cvar.set_value(saved_value)
_pending_cvar_values.erase(cvar.name)
## Coerces a string into the given Variant.Type. Returns [success : bool, value : Variant].
func _coerce_string_to_type(string_value : String, type : int) -> Array:
match type:
TYPE_BOOL:
var lower := string_value.strip_edges().to_lower()
if lower in ["1", "true", "yes", "on"]:
return [true, true]
if lower in ["0", "false", "no", "off"]:
return [true, false]
return [false, null]
TYPE_INT:
var int_string := string_value.strip_edges()
if int_string.is_valid_int():
return [true, int_string.to_int()]
return [false, null]
TYPE_FLOAT:
var float_string := string_value.strip_edges()
if float_string.is_valid_float():
return [true, float_string.to_float()]
return [false, null]
TYPE_STRING:
return [true, string_value]
TYPE_STRING_NAME:
return [true, StringName(string_value)]
_:
# Complex types (Vector2, etc.) require GDScript literal syntax, ex: "Vector2(1, 2)".
var parsed : Variant = str_to_var(string_value)
if parsed != null and typeof(parsed) == type:
return [true, parsed]
return [false, null]
func _print_cvar_value(cvar : ConsoleCvar) -> void:
print_line("%s = [system_color color=CONSOLE_COLOR_LITERAL]%s[/system_color]" % [cvar.name, str(cvar.get_value())])
func _handle_cvar(cvar : ConsoleCvar, arguments : PackedStringArray) -> void:
if not cvar.is_alive():
print_error("Variable \"%s\" references a freed object." % cvar.name)
return
if arguments.is_empty():
_print_cvar_value(cvar)
if cvar.description:
print_line("%s%s" % [tab_string, cvar.description])
return
var raw_value := " ".join(arguments)
var result := _coerce_string_to_type(raw_value, cvar.type)
if not result[0]:
print_error("Invalid value \"%s\" for variable \"%s\" (expected %s)." % [raw_value, cvar.name, type_string(cvar.type)])
return
cvar.set_value(result[1])
console_cvar_changed.emit(cvar.name, result[1])
_print_cvar_value(cvar)
## Useful if you have a list of possible parameters (ex: level names).
func add_command_autocomplete_list(command_name : String, param_list : PackedStringArray):
command_parameters[command_name] = param_list
func _enter_tree() -> void:
var console_history_file := FileAccess.open("user://console_history.txt", FileAccess.READ)
if (console_history_file):
while (!console_history_file.eof_reached()):
var line := console_history_file.get_line()
if (line.length()):
add_input_history(line)
# Load persisted cvar values. They're stashed until the matching cvar is registered.
var console_cvars_file := FileAccess.open("user://console_cvars.txt", FileAccess.READ)
if (console_cvars_file):
while (!console_cvars_file.eof_reached()):
var line := console_cvars_file.get_line()
if (line.length()):
var split := line.split(" ", true, 1) # "name var_to_str(value)"
if (split.size() == 2):
_pending_cvar_values[split[0]] = str_to_var(split[1])
if ProjectSettings.has_setting(CONSOLE_THEME):
theme = load(ProjectSettings.get_setting(CONSOLE_THEME))
if theme:
v_box_container.theme = theme
if ProjectSettings.has_setting(CONSOLE_TABSTOP):
tab_string = ""
for i in range(ProjectSettings.get_setting(CONSOLE_TABSTOP)):
tab_string += " "
canvas_layer.layer = ProjectSettings.get_setting(&"console/canvas_layer", 3)
add_child(canvas_layer)
console_scale = _get_console_scale_setting()
v_box_container.offset_bottom = 0
v_box_container.offset_left = 0
v_box_container.offset_right = 0
v_box_container.offset_top = 0
canvas_layer.add_child(v_box_container)
panel.size_flags_vertical = Control.SIZE_FILL ^ Control.SIZE_EXPAND
v_box_container.add_child(panel)
rich_label.selection_enabled = true
rich_label.context_menu_enabled = true
rich_label.bbcode_enabled = true
rich_label.scroll_following = true
rich_label.anchor_right = 1.0
rich_label.anchor_bottom = 1.0
rich_label.install_effect(preload("res://addons/console/system_color.gd").new())
panel.add_child(rich_label)
rich_label.append_text("Development console.\n")
line_edit.anchor_right = 1.0
line_edit.placeholder_text = "Enter \"help\" for instructions"
if font_size > 0:
line_edit.add_theme_font_size_override("font_size", font_size)
v_box_container.add_child(line_edit)
line_edit.text_submitted.connect(_on_text_entered)
line_edit.text_changed.connect(_on_line_edit_text_changed)
v_box_container.visible = false
process_mode = PROCESS_MODE_ALWAYS
## Get the scale of the console from the settings -- if this is not in the system settings return a default value
func _get_console_scale_setting() -> float:
if ProjectSettings.has_setting(CONSOLE_SCALE):
return ProjectSettings.get_setting(CONSOLE_SCALE)
return 1.0
## Set the console scale
func set_console_scale(scale : float):
console_scale = scale
v_box_container.scale = Vector2(console_scale, console_scale)
v_box_container.anchor_right = _get_console_width()
v_box_container.anchor_bottom = _get_console_height()
## Get the height of the console - this is related to the scaling of the console
func _get_console_height() -> float:
if console_full_screen:
return 1.0 / console_scale
if ProjectSettings.has_setting(CONSOLE_HEIGHT):
return ProjectSettings.get_setting(CONSOLE_HEIGHT) / console_scale
return 0.5 / console_scale
func _get_console_width() -> float:
return 1.0 / console_scale
func _set_font_size(value: int) -> void:
font_size = value
if value > 0:
line_edit.add_theme_font_size_override("font_size", font_size)
rich_label.add_theme_font_size_override("normal_font_size", font_size)
rich_label.add_theme_font_size_override("bold_font_size", font_size)
rich_label.add_theme_font_size_override("bold_italics_font_size", font_size)
rich_label.add_theme_font_size_override("italics_font_size", font_size)
rich_label.add_theme_font_size_override("mono_font_size", font_size)
else:
line_edit.remove_theme_font_size_override("font_size")
rich_label.remove_theme_font_size_override("normal_font_size")
rich_label.remove_theme_font_size_override("bold_font_size")
rich_label.remove_theme_font_size_override("bold_italics_font_size")
rich_label.remove_theme_font_size_override("italics_font_size")
rich_label.remove_theme_font_size_override("mono_font_size")
func _exit_tree() -> void:
var console_history_file := FileAccess.open("user://console_history.txt", FileAccess.WRITE)
if (console_history_file):
var write_index := 0
var start_write_index := console_history.size() - 100 # Max lines to write
for line in console_history:
if (write_index >= start_write_index):
console_history_file.store_line(line)
write_index += 1
var console_cvars_file := FileAccess.open("user://console_cvars.txt", FileAccess.WRITE)
if (console_cvars_file):
for cvar_name in console_cvars:
var cvar : ConsoleCvar = console_cvars[cvar_name]
if (cvar.save and cvar.is_alive()):
console_cvars_file.store_line("%s %s" % [cvar_name, var_to_str(cvar.get_value())])
for pending_name in _pending_cvar_values:
if (!console_cvars.has(pending_name)):
console_cvars_file.store_line("%s %s" % [pending_name, var_to_str(_pending_cvar_values[pending_name])])
func _ready() -> void:
v_box_container.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST # For that retro look.
add_command("quit", quit, 0, 0, "Quits the game.")
add_command("exit", quit, 0, 0, "Quits the game.")
add_command("clear", clear, 0, 0, "Clears the text on the console.")
add_command("delete_history", delete_history, 0, 0, "Deletes the history of previously entered commands.")
add_command("help", help, 0, 0, "Displays instructions on how to use the console.")
add_command("commands_list", commands_list, 0, 0, "Lists all commands and their descriptions.")
add_command("commands", commands, 0, 0, "Lists commands with no descriptions.")
add_command("cvars", cvars, 0, 0, "Lists all console variables and their values.")
add_command("calc", calculate, ["mathematical expression to evaluate"], 0, "Evaluates the math passed in for quick arithmetic.")
add_command("echo", print_line, ["string"], 1, "Prints given string to the console.")
add_command("echo_warning", print_warning, ["string"], 1, "Prints given string as warning to the console.")
add_command("echo_info", print_info, ["string"], 1, "Prints given string as info to the console.")
add_command("echo_error", print_error, ["string"], 1, "Prints given string as an error to the console.")
add_command("pause", pause, 0, 0, "Pauses node processing.")
add_command("unpause", unpause, 0, 0, "Unpauses node processing.")
add_command("exec", exec, 1, 1, "Execute a script.")
func _input(event : InputEvent) -> void:
if (event is InputEventKey):
if (event.physical_keycode == KEY_QUOTELEFT): # ~ key.
if (event.physical_keycode == KEY_QUOTELEFT and event.is_command_or_control_pressed()): # Toggles console size or opens big console.
if (event.pressed):
if (v_box_container.visible):
toggle_size()
else:
toggle_console()
toggle_size()
get_tree().get_root().set_input_as_handled()
else:
if (event.pressed):
toggle_console()
get_tree().get_root().set_input_as_handled()
elif (event.get_physical_keycode_with_modifiers() == KEY_ESCAPE && v_box_container.visible): # Disable console on ESC
if (event.pressed):
toggle_console()
get_tree().get_root().set_input_as_handled()
if (v_box_container.visible and event.pressed):
if (event.get_physical_keycode_with_modifiers() == KEY_UP):
get_tree().get_root().set_input_as_handled()
if (console_history_index > 0):
console_history_index -= 1
if (console_history_index >= 0):
line_edit.text = console_history[console_history_index]
line_edit.caret_column = line_edit.text.length()
reset_autocomplete()
if (event.get_physical_keycode_with_modifiers() == KEY_DOWN):
get_tree().get_root().set_input_as_handled()
if (console_history_index < console_history.size()):
console_history_index += 1
if (console_history_index < console_history.size()):
line_edit.text = console_history[console_history_index]
line_edit.caret_column = line_edit.text.length()
reset_autocomplete()
else:
line_edit.text = ""
reset_autocomplete()
if (event.get_physical_keycode_with_modifiers() == KEY_PAGEUP):
var scroll := rich_label.get_v_scroll_bar()
var tween := create_tween()
tween.tween_property(scroll, "value", scroll.value - (scroll.page - scroll.page * 0.1), 0.1)
get_tree().get_root().set_input_as_handled()
if (event.get_physical_keycode_with_modifiers() == KEY_PAGEDOWN):
var scroll := rich_label.get_v_scroll_bar()
var tween := create_tween()
tween.tween_property(scroll, "value", scroll.value + (scroll.page - scroll.page * 0.1), 0.1)
get_tree().get_root().set_input_as_handled()
if (event.get_physical_keycode_with_modifiers() == KEY_TAB):
autocomplete()
get_tree().get_root().set_input_as_handled()
elif event is InputEventMouseButton:
if (v_box_container.visible):
if (event.is_command_or_control_pressed()):
if event.button_index == MOUSE_BUTTON_WHEEL_UP: # Increase font size with ctrl+mouse wheel up
font_size = min(128, font_size + 2) # Limit to max of 128
get_tree().get_root().set_input_as_handled()
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: # Decrease font size with ctrl+mouse wheel down
font_size = max(8, font_size - 2) # Limit to minimum of 8
get_tree().get_root().set_input_as_handled()
var suggestions := []
var current_suggest := 0
var suggesting := false
func autocomplete() -> void:
if (suggesting):
for i in range(suggestions.size()):
if (current_suggest == i):
line_edit.text = str(suggestions[i])
line_edit.caret_column = line_edit.text.length()
if (current_suggest == suggestions.size() - 1):
current_suggest = 0
else:
current_suggest += 1
return
else:
suggesting = true
if (" " in line_edit.text): # We're searching for a parameter to autocomplete
var split_text := parse_line_input(line_edit.text)
if (split_text.size() > 1):
var command := split_text[0]
var param_input := split_text[1]
if (command_parameters.has(command)):
for param in command_parameters[command]:
if (param_input in param) or (param_input.length() == 0):
suggestions.append(str(command, " ", param))
else:
var sorted_commands := []
for command in console_commands:
if (!console_commands[command].hidden):
sorted_commands.append(str(command))
for cvar_name in console_cvars:
sorted_commands.append(str(cvar_name))
sorted_commands.sort()
sorted_commands.reverse()
var prev_index := 0
for command in sorted_commands:
if (!line_edit.text || command.contains(line_edit.text)):
var index : int = command.find(line_edit.text)
if (index <= prev_index):
suggestions.push_front(command)
else:
suggestions.push_back(command)
prev_index = index
autocomplete()
func reset_autocomplete() -> void:
suggestions.clear()
current_suggest = 0
suggesting = false
func toggle_size() -> void:
console_full_screen = !console_full_screen
v_box_container.anchor_bottom = _get_console_height()
func disable():
enabled = false
toggle_console() # Ensure hidden if opened
func enable():
enabled = true
func toggle_console() -> void:
var was_visible := v_box_container.visible
if (enabled):
v_box_container.visible = !v_box_container.visible
else:
v_box_container.visible = false
if (v_box_container.visible):
was_paused_already = get_tree().paused
get_tree().paused = was_paused_already || pause_enabled
line_edit.grab_focus()
if was_visible != v_box_container.visible:
console_opened.emit()
else:
scroll_to_bottom()
reset_autocomplete()
if (pause_enabled && !was_paused_already):
get_tree().paused = false
if was_visible != v_box_container.visible:
console_closed.emit()
func is_visible():
return v_box_container.visible
func scroll_to_bottom() -> void:
var scroll: ScrollBar = rich_label.get_v_scroll_bar()
scroll.value = scroll.max_value - scroll.page
func print_error(text : Variant, print_godot := false) -> void:
var _color : Color = Color.LIGHT_CORAL
if not text is String:
text = str(text)
print_line("%s[system_color color=CONSOLE_COLOR_ERROR]ERROR:[/system_color] %s" % [tab_string, text], print_godot)
func print_info(text : Variant, print_godot := false) -> void:
var _color : Color = Color.LIGHT_BLUE
if not text is String:
text = str(text)
print_line("%s[system_color color=CONSOLE_COLOR_INFO]INFO:[/system_color] %s" % [tab_string, text], print_godot)
func print_warning(text : Variant, print_godot := false) -> void:
var _color : Color = Color.LIGHT_GOLDENROD
if not text is String:
text = str(text)
print_line("%s[system_color color=CONSOLE_COLOR_WARNING]WARNING:[/system_color] %s" % [tab_string, text], print_godot)
func print_line(text : Variant, print_godot := false) -> void:
if not text is String:
text = str(text)
if (!rich_label): # Tried to print something before the console was loaded.
call_deferred("print_line", text)
else:
rich_label.append_text(text)
rich_label.append_text("\n")
if (print_godot):
print_rich(text.dedent())
func parse_line_input(text : String) -> PackedStringArray:
var out_array : PackedStringArray
var first_char := true
var in_quotes := false
var escaped := false
var token : String
for c in text:
if (c == '\\'):
escaped = true
continue
elif (escaped):
if (c == 'n'):
c = '\n'
elif (c == 't'):
c = '\t'
elif (c == 'r'):
c = '\r'
elif (c == 'a'):
c = '\a'
elif (c == 'b'):
c = '\b'
elif (c == 'f'):
c = '\f'
escaped = false
elif (c == '\"'):
in_quotes = !in_quotes
continue
elif (c == ' ' || c == '\t'):
if (!in_quotes):
out_array.push_back(token)
token = ""
continue
token += c
out_array.push_back(token)
return out_array
func _on_text_entered(new_text : String) -> void:
scroll_to_bottom()
reset_autocomplete()
line_edit.clear()
if (line_edit.has_method(&"edit")):
line_edit.call_deferred(&"edit")
if not new_text.strip_edges().is_empty():
add_input_history(new_text)
print_line("[i]> " + new_text + "[/i]")
var text_split := parse_line_input(new_text)
var text_command := text_split[0]
if console_commands.has(text_command):
var arguments := text_split.slice(1)
var console_command : ConsoleCommand = console_commands[text_command]
# calc is a especial command that needs special treatment
if (text_command.match("calc")):
var expression := ""
for word in arguments:
expression += word
console_command.function.callv([expression])
return
if (arguments.size() < console_command.required):
print_error("Too few arguments! Required < %d >" % console_command.required)
return
elif (arguments.size() > console_command.arguments.size()):
arguments.resize(console_command.arguments.size())
# Functions fail to call if passed the incorrect number of arguments, so fill out with blank strings.
while (arguments.size() < console_command.arguments.size()):
arguments.append("")
console_command.function.callv(arguments)
elif console_cvars.has(text_command):
_handle_cvar(console_cvars[text_command], text_split.slice(1))
else:
console_unknown_command.emit(text_command)
print_error("Unknown command or variable.")
func _on_line_edit_text_changed(new_text : String) -> void:
reset_autocomplete()
func quit() -> void:
get_tree().quit()
func clear() -> void:
rich_label.clear()
func delete_history() -> void:
console_history.clear()
console_history_index = 0
DirAccess.remove_absolute("user://console_history.txt")
func help() -> void:
rich_label.append_text(" Built in commands:
[system_color color=CONSOLE_COLOR_LITERAL]calc[/system_color]: Calculates a given expresion
[system_color color=CONSOLE_COLOR_LITERAL]clear[/system_color]: Clears the registry view
[system_color color=CONSOLE_COLOR_LITERAL]commands[/system_color]: Shows a reduced list of all the currently registered commands
[system_color color=CONSOLE_COLOR_LITERAL]commands_list[/system_color]: Shows a detailed list of all the currently registered commands
[system_color color=CONSOLE_COLOR_LITERAL]cvars[/system_color]: Lists all console variables and their values
[system_color color=CONSOLE_COLOR_LITERAL]delete_history[/system_color]: Deletes the commands history
[system_color color=CONSOLE_COLOR_LITERAL]echo[/system_color]: Prints a given string to the console
[system_color color=CONSOLE_COLOR_LITERAL]echo_error[/system_color]: Prints a given string as an error to the console
[system_color color=CONSOLE_COLOR_LITERAL]echo_info[/system_color]: Prints a given string as info to the console
[system_color color=CONSOLE_COLOR_LITERAL]echo_warning[/system_color]: Prints a given string as warning to the console
[system_color color=CONSOLE_COLOR_LITERAL]pause[/system_color]: Pauses node processing
[system_color color=CONSOLE_COLOR_LITERAL]unpause[/system_color]: Unpauses node processing
[system_color color=CONSOLE_COLOR_LITERAL]quit[/system_color]: Quits the game
Controls:
[system_color color=CONSOLE_COLOR_INFO]Up[/system_color] and [system_color color=CONSOLE_COLOR_INFO]Down[/system_color] arrow keys to navigate commands history
[system_color color=CONSOLE_COLOR_INFO]PageUp[/system_color] and [system_color color=CONSOLE_COLOR_INFO]PageDown[/system_color] to scroll registry
[[system_color color=CONSOLE_COLOR_INFO]Ctrl[/system_color] + [system_color color=CONSOLE_COLOR_INFO]~[/system_color]] to change console size between half screen and full screen
[[system_color color=CONSOLE_COLOR_INFO]Ctrl[/system_color] + [system_color color=CONSOLE_COLOR_INFO]Mouse Wheel[/system_color]] up/down to change console font size
[system_color color=CONSOLE_COLOR_INFO]~[/system_color] or [system_color color=CONSOLE_COLOR_INFO]Esc[/system_color] key to close the console
[system_color color=CONSOLE_COLOR_INFO]Tab[/system_color] key to autocomplete, [system_color system_color=CONSOLE_COLOR_INFO]Tab[/system_color] again to cycle between matching suggestions\n\n")
func calculate(command : String) -> void:
var expression := Expression.new()
var error = expression.parse(command)
if error:
print_error("%s" % expression.get_error_text())
return
var result = expression.execute()
if not expression.has_execute_failed():
print_line(str(result))
else:
print_error("%s" % expression.get_error_text())
func commands() -> void:
var commands := []
for command in console_commands:
if (!console_commands[command].hidden):
commands.append(str(command))
commands.sort()
rich_label.append_text(" ")
rich_label.append_text(str(commands) + "\n\n")
func commands_list() -> void:
var commands := []
for command in console_commands:
if (!console_commands[command].hidden):
commands.append(str(command))
commands.sort()
for command in commands:
var arguments_string := ""
var description : String = console_commands[command].description
for i in range(console_commands[command].arguments.size()):
if i < console_commands[command].required:
arguments_string += " [system_color color=CONSOLE_COLOR_ERROR]<" + console_commands[command].arguments[i] + ">[/system_color]"
else:
arguments_string += " [system_color color=CONSOLE_COLOR_INFO]<" + console_commands[command].arguments[i] + ">[/system_color]"
rich_label.append_text(" [system_color color=CONSOLE_COLOR_LITERAL]%s[/system_color]%s: %s\n" % [command, arguments_string, description])
rich_label.append_text("\n")
func cvars() -> void:
var names := console_cvars.keys()
names.sort()
for cvar_name in names:
var cvar : ConsoleCvar = console_cvars[cvar_name]
var value_string := "<invalid>"
if (cvar.is_alive()):
value_string = str(cvar.get_value())
rich_label.append_text(" [system_color color=CONSOLE_COLOR_LITERAL]%s[/system_color] = [system_color color=CONSOLE_COLOR_INFO]%s[/system_color] %s\n" % [cvar_name, value_string, cvar.description])
rich_label.append_text("\n")
func add_input_history(text : String) -> void:
if (!console_history.size() || text != console_history.back()): # Don't add consecutive duplicates
console_history.append(text)
console_history_index = console_history.size()
func set_enable_on_release_build(enable : bool):
enable_on_release_build = enable
if (!enable_on_release_build):
if (!OS.is_debug_build()):
disable()
func pause() -> void:
get_tree().paused = true
func unpause() -> void:
get_tree().paused = false
func exec(filename : String) -> void:
var path := "user://%s.txt" % [filename]
var script := FileAccess.open(path, FileAccess.READ)
if (script):
while (!script.eof_reached()):
_on_text_entered(script.get_line())
else:
print_error("File %s not found." % [path])
+1
View File
@@ -0,0 +1 @@
uid://ouiu5xh1cs8n
+108
View File
@@ -0,0 +1,108 @@
@tool
extends EditorPlugin
const SINGLETON_NAME = &"Console"
##FIXME: These should be maintained in one place.
const CONSOLE_THEME : String = &"console/theme"
const CONSOLE_SCALE : String = &"console/scale"
const CONSOLE_HEIGHT : String = &"console/height"
const CONSOLE_COLOR_WARNING : String = &"console/color_warning"
const CONSOLE_COLOR_ERROR : String = &"console/color_error"
const CONSOLE_COLOR_INFO : String = &"console/color_info"
const CONSOLE_COLOR_LITERAL : String = &"console/color_literal"
const CONSOLE_TABSTOP : String = &"console/tabstop"
const CONSOLE_CANVAS_LAYER : String = &"console/canvas_layer"
##FIXME: This is here because project settings do no return the default value naturally
##This should be fixed in 4.5
const color_dictionary : Dictionary[String, Color] = {
CONSOLE_COLOR_ERROR: Color.LIGHT_CORAL,
CONSOLE_COLOR_INFO: Color.LIGHT_BLUE,
CONSOLE_COLOR_LITERAL: Color.PALE_GREEN,
CONSOLE_COLOR_WARNING: Color.LIGHT_GOLDENROD
}
func add_setting(setting_name: String, property_info: Dictionary, default: Variant) -> void:
if not ProjectSettings.has_setting(setting_name):
ProjectSettings.set_setting(setting_name, default)
ProjectSettings.add_property_info(property_info)
ProjectSettings.set_initial_value(setting_name, default)
ProjectSettings.set_as_basic(setting_name, true)
func _setup_project_settings() -> void:
## Configure Console Theme
add_setting(CONSOLE_THEME, {
"name": CONSOLE_THEME,
"type": TYPE_STRING,
"hint": PROPERTY_HINT_FILE,
"hint_string": "*.tres",
}, "")
## Configure Console Scale
add_setting(CONSOLE_SCALE, {
"name": CONSOLE_SCALE,
"type": TYPE_FLOAT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,10,0.1,or_greater"
}, 1.0)
## Configure Console Height
add_setting(CONSOLE_HEIGHT, {
"name": CONSOLE_HEIGHT,
"type": TYPE_FLOAT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,1,0.1"
}, 0.5)
## Configure Tab Spaces
add_setting(CONSOLE_TABSTOP, {
"name": CONSOLE_TABSTOP,
"type": TYPE_INT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "0,8,1,or_greater"
},4)
## Configure Canvas Layer
add_setting(CONSOLE_CANVAS_LAYER, {
"name": CONSOLE_CANVAS_LAYER,
"type": TYPE_INT,
}, 3)
#Configure Colors
add_setting(CONSOLE_COLOR_ERROR, {
"name": CONSOLE_COLOR_ERROR,
"type": TYPE_COLOR,
"hint": PROPERTY_HINT_COLOR_NO_ALPHA,
}, color_dictionary[CONSOLE_COLOR_ERROR])
add_setting(CONSOLE_COLOR_INFO, {
"name": CONSOLE_COLOR_INFO,
"type": TYPE_COLOR,
"hint": PROPERTY_HINT_COLOR_NO_ALPHA,
}, color_dictionary[CONSOLE_COLOR_INFO])
add_setting(CONSOLE_COLOR_WARNING, {
"name": CONSOLE_COLOR_WARNING,
"type": TYPE_COLOR,
"hint": PROPERTY_HINT_COLOR_NO_ALPHA,
}, color_dictionary[CONSOLE_COLOR_WARNING])
add_setting(CONSOLE_COLOR_LITERAL, {
"name": CONSOLE_COLOR_LITERAL,
"type": TYPE_COLOR,
"hint": PROPERTY_HINT_COLOR_NO_ALPHA
}, color_dictionary[CONSOLE_COLOR_LITERAL])
ProjectSettings.save()
func _enable_plugin() -> void:
add_autoload_singleton(SINGLETON_NAME, "res://addons/console/console.gd")
_setup_project_settings()
print("Console plugin enabled.")
func _disable_plugin() -> void:
remove_autoload_singleton(SINGLETON_NAME)
print("Console plugin disabled.")
+1
View File
@@ -0,0 +1 @@
uid://cv2joe2dgkub1
+65
View File
@@ -0,0 +1,65 @@
using Godot;
using Godot.Collections;
public partial class Console : Node{
static GodotObject console = (GodotObject)GD.Load<GDScript>("uid://ouiu5xh1cs8n").New();
public static bool Enabled{ get{return console.Get("enabled").AsBool();} }
public static bool EnabledOnReleaseBuild{ get{return console.Get("enabled_on_release_build").AsBool();} }
public static bool PauseEnabled{ get{return console.Get("pause_enabled").AsBool();} set{console.Set("pause_enabled", value);} }
public static int FontSize{ get{return console.Get("font_size").AsInt32();} set{console.Set("font_size", value);} }
public static RichTextLabel RichLabel{ get{return (RichTextLabel)console.Get("rich_label").AsGodotObject();} set{console.Set("rich_label", value);} }
public static LineEdit LineEdit{ get{return (LineEdit)console.Get("line_edit").AsGodotObject();} set{console.Set("line_edit", value);} }
public override void _Ready(){
AddChild((Node)console);
}
public static void ConnectConsoleOpened(Callable callable) => console.Connect("console_opened", callable);
public static void DisconnectConsoleOpened(Callable callable) => console.Disconnect("console_opened", callable);
public static void ConnectConsoleClosed(Callable callable) => console.Connect("console_closed", callable);
public static void DisconnectConsoleClosed(Callable callable) => console.Disconnect("console_closed", callable);
public static void ConnectConsoleUnknownCommand(Callable callable) => console.Connect("console_unknown_command", callable);
public static void DisconnectConsoleUnknownCommand(Callable callable) => console.Disconnect("console_unknown_command", callable);
public static void ConnectConsoleCvarChanged(Callable callable) => console.Connect("console_cvar_changed", callable);
public static void DisconnectConsoleCvarChanged(Callable callable) => console.Disconnect("console_cvar_changed", callable);
public static void AddCommand(string commandName, Callable function, Array<string> arguments, int required = 0, string description = "") => console.Call("add_command", commandName, function, arguments, required, description);
public static void AddHiddenCommand(string commandName, Callable function, Array<string> arguments, int required) => console.Call("add_hidden_command", commandName, function, arguments, required);
public static void RemoveCommand(string commandName) => console.Call("remove_command", commandName);
public static void AddCvar(string cvarName, Variant defaultValue, string description = "", bool save = false) => console.Call("add_cvar", cvarName, defaultValue, description, save);
public static void AddCvarReference(string cvarName, GodotObject @object, string property, string description = "", bool save = false) => console.Call("add_cvar_reference", cvarName, @object, property, description, save);
public static void RemoveCvar(string cvarName) => console.Call("remove_cvar", cvarName);
public static Variant GetCvar(string cvarName) => console.Call("get_cvar", cvarName);
public static void SetCvar(string cvarName, Variant value) => console.Call("set_cvar", cvarName, value);
public static void AddCommandAutocompleteList(string commandName, string[] paramList) => console.Call("add_command_autocomplete_list", commandName, paramList);
public static void Disable() => console.Call("disable");
public static void Enable() =>console.Call("enable");
public static void ToggleConsole() => console.Call("toggle_console");
public static void IsVisible() => console.Call("is_visible");
public static void ScrollToBottom() => console.Call("scroll_to_bottom");
public static void PrintError(Variant text, bool printGodot = false) => console.Call("print_error", text, printGodot);
public static void PrintInfo(Variant text, bool printGodot = false) => console.Call("print_info", text, printGodot);
public static void PrintWarning(Variant text, bool printGodot = false) => console.Call("print_warning", text, printGodot);
public static void PrintLine(Variant text, bool printGodot = false) => console.Call("print_line", text, printGodot);
public static void SetEnableOnReleaseBuild(bool enable) => console.Call("set_enable_on_release_build", enable);
/*
public static void Autocomplete() => console.Call("autocomplete");
public static void ResetAutocomplete() => console.Call("reset_autocomplete");
public static void ToggleSize() => console.Call("toggle_size");
public static string[] ParseLineInput(string text){ return console.Call("parse_line_input", text).AsStringArray(); }
public static void Quit() => console.Call("quit");
public static void Clear() => console.Call("clear");
public static void DeleteHistory() => console.Call("delete_history");
public static void Help() => console.Call("help");
public static void Calculate() => console.Call("calculate");
public static void Commands() => console.Call("commands");
public static void CommandsList() => console.Call("commands_list");
public static void Cvars() => console.Call("cvars");
public static void Pause() => console.Call("pause");
public static void Unpause() => console.Call("unpause");
public static void Exec(string fileName) => console.Call("exec", filename);
*/
}
@@ -0,0 +1 @@
uid://bdi2oxut3dsra
+7
View File
@@ -0,0 +1,7 @@
[plugin]
name="Developer Console"
description="Developer console. Press ~ to activate it in game and execute commands."
author="jitspoe"
version="1.5.0"
script="console_plugin.gd"
+29
View File
@@ -0,0 +1,29 @@
@tool
extends RichTextEffect
class_name SystemColor
var bbcode : String = "system_color"
func _process_custom_fx(char_fx : CharFXTransform):
var _system_color = char_fx.env.get("color")
if not _system_color:
return false
if not _system_color in Console:
return false
# Convert string to const value
_system_color = Console.get_script().get_script_constant_map()[_system_color]
var _color : Color = Console.color_dictionary[_system_color]
if ProjectSettings.has_setting(_system_color):
_color = ProjectSettings.get_setting(_system_color)
if not _color:
return false
char_fx.color = _color
return true
+1
View File
@@ -0,0 +1 @@
uid://cw62jxvqhw333
+119
View File
@@ -1,6 +1,8 @@
[runnable_presets]
Web="Web"
Linux="Linux"
"Windows Desktop"="Windows Desktop"
[preset.0]
@@ -53,3 +55,120 @@ threads/godot_pool_size=4
dotnet/include_scripts_content=true
dotnet/include_debug_symbols=true
dotnet/embed_build_outputs=true
[preset.1]
name="Linux"
platform="Linux"
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter="Build/*"
export_path="Build/scavgame.x86_64"
patches=PackedStringArray()
patch_delta_encoding=false
patch_delta_compression_level_zstd=19
patch_delta_min_reduction=0.1
patch_delta_include_filters="*"
patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.1.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=false
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=true
binary_format/architecture="x86_64"
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="#!/usr/bin/env bash
export DISPLAY=:0
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
pkill -x -f \"{temp_dir}/{exe_name} {cmd_args}\"
rm -rf \"{temp_dir}\""
[preset.2]
name="Windows Desktop"
platform="Windows Desktop"
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter="Build/*"
export_path="Build/scavgame.exe"
patches=PackedStringArray()
patch_delta_encoding=false
patch_delta_compression_level_zstd=19
patch_delta_min_reduction=0.1
patch_delta_include_filters="*"
patch_delta_exclude_filters=""
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.2.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=false
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"
+16 -1
View File
@@ -11,21 +11,32 @@ config_version=5
[application]
config/name="scavgame"
config/version="1.0.0"
run/main_scene="uid://cjwfyvtoeaqid"
config/use_custom_user_dir=true
config/custom_user_dir_name="Astronand/scavgamedemo"
config/features=PackedStringArray("4.7")
boot_splash/show_image=false
config/icon="res://icon.svg"
[autoload]
Console="*uid://ouiu5xh1cs8n"
[display]
window/size/viewport_width=1920
window/size/viewport_height=1080
window/size/borderless=true
[dotnet]
project/assembly_name="Scavenger"
[editor_plugins]
enabled=PackedStringArray("res://addons/console/plugin.cfg")
[layer_names]
3d_physics/layer_1="Physics"
@@ -38,4 +49,8 @@ project/assembly_name="Scavenger"
[rendering]
renderer/rendering_method="mobile"
renderer/rendering_method="gl_compatibility"
[webrtc]
default_extension="res://addons/webrtc_native/webrtc_native.gdextension"