Phase 7: netfox + godot-jolt stack upgrade
Stack installed: - netfox v1.35.3 (core + extras + noray + internals) - godot-jolt v0.16.0-stable Architecture: - Server: ENet transport (works headless, no netfox deps) - Client/Editor: netfox rollback (RollbackSynchronizer, TickInterpolator) New/modified: - docs/migration-netfox-plan.md — migration architecture - scripts/network/network_manager.gd — netfox-aware ENet fallback - scripts/network/player.gd — clean base player - client/characters/player_netfox.gd — rollback player w/ WeaponManager - client/characters/input/player_net_input.gd — BaseNetInput subclass - client/characters/character/fps_character_controller.gd — netfox input feed - client/weapons/ — weapon data, registry, TacticalWeaponHitscan, WeaponManager - client/scripts/round_replicator.gd — client-side round state bridge - server/scripts/round_manager.gd — improved state machine - server/scripts/plugin_api/plugin_manager.gd — refined plugin system - config: enemy_tag, ally_tag for meatball targeting Removed: old C++ SimulationServer GDExtension (replaced by netfox rollback)
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
extends Node3D
|
||||
class_name NetworkWeaponHitscan3D
|
||||
|
||||
## A 3D-specific implementation of a networked hitscan (raycast) weapon.
|
||||
|
||||
## Maximum distance to cast the ray
|
||||
@export var max_distance: float = 1000.0
|
||||
|
||||
## Mask used to detect raycast hits
|
||||
@export_flags_3d_physics var collision_mask: int = 0xFFFFFFFF
|
||||
|
||||
## Colliders excluded from raycast hits
|
||||
@export var exclude: Array[RID] = []
|
||||
|
||||
var _weapon: _NetworkWeaponProxy
|
||||
|
||||
## Try to fire the weapon and return the projectile.
|
||||
## [br][br]
|
||||
## Returns true if the weapon was fired.
|
||||
func fire() -> bool:
|
||||
if not can_fire():
|
||||
return false
|
||||
|
||||
_apply_data(_get_data())
|
||||
_after_fire()
|
||||
return true
|
||||
|
||||
## Check whether this weapon can be fired.
|
||||
func can_fire() -> bool:
|
||||
return _weapon.can_fire()
|
||||
|
||||
func _init():
|
||||
_weapon = _NetworkWeaponProxy.new()
|
||||
add_child(_weapon, true, INTERNAL_MODE_BACK)
|
||||
_weapon.owner = self
|
||||
|
||||
_weapon.c_can_fire = _can_fire
|
||||
_weapon.c_can_peer_use = _can_peer_use
|
||||
_weapon.c_after_fire = _after_fire
|
||||
_weapon.c_spawn = _spawn
|
||||
_weapon.c_get_data = _get_data
|
||||
_weapon.c_apply_data = _apply_data
|
||||
_weapon.c_is_reconcilable = _is_reconcilable
|
||||
_weapon.c_reconcile = _reconcile
|
||||
|
||||
## Override this method with your own can fire logic.
|
||||
## [br][br]
|
||||
## See [NetworkWeapon].
|
||||
func _can_fire() -> bool:
|
||||
return true
|
||||
|
||||
## Override this method to check if a given peer can use this weapon.
|
||||
## [br][br]
|
||||
## See [NetworkWeapon].
|
||||
func _can_peer_use(peer_id: int) -> bool:
|
||||
return true
|
||||
|
||||
## Override this method to run any logic needed after successfully firing the
|
||||
## weapon.
|
||||
## [br][br]
|
||||
## See [NetworkWeapon].
|
||||
func _after_fire():
|
||||
pass
|
||||
|
||||
func _spawn():
|
||||
# No projectile is spawned for a hitscan weapon.
|
||||
pass
|
||||
|
||||
func _get_data() -> Dictionary:
|
||||
# Collect data needed to synchronize the firing event.
|
||||
return {
|
||||
"origin": global_transform.origin,
|
||||
"direction": -global_transform.basis.z # Assuming forward direction.
|
||||
}
|
||||
|
||||
func _apply_data(data: Dictionary):
|
||||
# Reproduces the firing event on all peers.
|
||||
var origin = data["origin"] as Vector3
|
||||
var direction = data["direction"] as Vector3
|
||||
|
||||
# Perform the raycast from origin in the given direction.
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
|
||||
# Create a PhysicsRayQueryParameters3D object.
|
||||
var ray_params = PhysicsRayQueryParameters3D.new()
|
||||
ray_params.from = origin
|
||||
ray_params.to = origin + direction * max_distance
|
||||
|
||||
# Set collision masks or exclude objects:
|
||||
ray_params.collision_mask = collision_mask
|
||||
ray_params.exclude = exclude
|
||||
|
||||
var result = space_state.intersect_ray(ray_params)
|
||||
|
||||
if result:
|
||||
# Handle the hit result, such as spawning hit effects.
|
||||
_on_hit(result)
|
||||
|
||||
# Play firing effects on all peers.
|
||||
_on_fire()
|
||||
|
||||
func _is_reconcilable(request_data: Dictionary, local_data: Dictionary) -> bool:
|
||||
# Always reconcilable
|
||||
return true
|
||||
|
||||
func _reconcile(local_data: Dictionary, remote_data: Dictionary):
|
||||
# Nothing to do on reconcile
|
||||
pass
|
||||
|
||||
## Override to implement raycast hit logic.
|
||||
## [br][br]
|
||||
## The parameter is the result of a
|
||||
## [method PhysicsDirectSpaceState3D.intersect_ray] call.
|
||||
func _on_hit(result: Dictionary):
|
||||
# Implement hit effect logic here.
|
||||
# var hit_position = result.position
|
||||
# var hit_normal = result.normal
|
||||
# var collider = result.collider
|
||||
|
||||
# For example, you might emit a signal or instantiate a hit effect scene:
|
||||
# emit_signal("hit_detected", hit_position, hit_normal, collider)
|
||||
pass
|
||||
|
||||
## Override to implement firing effects, like muzzle flash or sound.
|
||||
func _on_fire():
|
||||
# Implement firing effect logic here.
|
||||
pass
|
||||
Reference in New Issue
Block a user