Implemented physics interpolation
This commit is contained in:
parent
ca7fa120ea
commit
489a14061a
@ -11,6 +11,6 @@ var terrain: Terrain3D
|
||||
var instance_id: int
|
||||
|
||||
func _enter_tree():
|
||||
instance_id = 0
|
||||
instance_id = OS.get_process_id() % 2
|
||||
account = Account.new()
|
||||
account.name = "user%d" % instance_id
|
||||
|
@ -28,6 +28,7 @@ process_thread_group = 2
|
||||
process_thread_group_order = 0
|
||||
process_thread_messages = 0
|
||||
script = ExtResource("2_8hxcx")
|
||||
host = "akyoto.dev"
|
||||
|
||||
[node name="Ping" type="Node" parent="Client"]
|
||||
unique_name_in_owner = true
|
||||
@ -84,8 +85,6 @@ process_thread_group_order = 0
|
||||
process_thread_messages = 0
|
||||
autostart = true
|
||||
|
||||
[node name="Camera" parent="." instance=ExtResource("12_aljdh")]
|
||||
|
||||
[node name="World" parent="." instance=ExtResource("13_sqmhj")]
|
||||
process_thread_group = 2
|
||||
process_thread_group_order = 0
|
||||
@ -95,8 +94,20 @@ process_thread_messages = 0
|
||||
unique_name_in_owner = true
|
||||
script = ExtResource("16_dp6bj")
|
||||
|
||||
[node name="UpdatePlayers" type="Timer" parent="Players"]
|
||||
unique_name_in_owner = true
|
||||
process_priority = 1
|
||||
wait_time = 0.25
|
||||
autostart = true
|
||||
|
||||
[node name="Camera" parent="." instance=ExtResource("12_aljdh")]
|
||||
process_priority = 100
|
||||
process_physics_priority = 100
|
||||
|
||||
[node name="UI" parent="." instance=ExtResource("17_43qhq")]
|
||||
process_mode = 3
|
||||
process_priority = 100
|
||||
process_physics_priority = 100
|
||||
|
||||
[connection signal="timeout" from="Client/Ping/Timer" to="Client/Ping" method="send_ping"]
|
||||
[connection signal="timeout" from="Client/Login/Timer" to="Client/Login" method="send_login"]
|
||||
|
@ -1,5 +1,5 @@
|
||||
class_name DeathComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
@export var health: HealthComponent
|
||||
@export var hud: HUDComponent
|
||||
@ -10,7 +10,7 @@ extends Node
|
||||
var respawn_position: Vector3
|
||||
|
||||
func _ready():
|
||||
respawn_position = owner.global_position
|
||||
respawn_position = character.global_position
|
||||
health.death.connect(on_death)
|
||||
|
||||
func on_death():
|
||||
@ -21,20 +21,20 @@ func on_death():
|
||||
|
||||
func drop_loot():
|
||||
var loot := drop.instantiate() as Node3D
|
||||
owner.get_parent().add_child(loot)
|
||||
loot.global_position = owner.global_position + loot.position
|
||||
character.get_parent().add_child(loot)
|
||||
loot.global_position = character.global_position + loot.position
|
||||
|
||||
func die():
|
||||
DeathComponent.set_physics(owner, false)
|
||||
DeathComponent.set_physics(character, false)
|
||||
hud.visible = false
|
||||
animation.play("slime_death")
|
||||
|
||||
func revive():
|
||||
health.restore()
|
||||
DeathComponent.set_physics(owner, true)
|
||||
DeathComponent.set_physics(character, true)
|
||||
hud.visible = true
|
||||
(owner as Node3D).transform = Transform3D.IDENTITY
|
||||
owner.global_position = respawn_position
|
||||
character.transform = Transform3D.IDENTITY
|
||||
character.global_position = respawn_position
|
||||
animation.play("slime_idle")
|
||||
|
||||
static func set_physics(obj: Node3D, alive: bool):
|
||||
|
@ -24,7 +24,7 @@ func _process(delta):
|
||||
if !collected_by:
|
||||
return
|
||||
|
||||
global_position = Math.damp(global_position, Global.player.global_position + Vector3.UP, 0.75 * delta)
|
||||
global_position = Math.damp_vector(global_position, Global.player.global_position + Vector3.UP, 0.75 * delta)
|
||||
|
||||
func on_body_entered(body: Node3D):
|
||||
if not body is Player:
|
||||
|
@ -1,17 +1,20 @@
|
||||
class_name Math
|
||||
|
||||
static func damp(from: Variant, to: Variant, weight: float):
|
||||
static func damp(from: Variant, to: Variant, weight: float) -> Variant:
|
||||
return lerp(from, to, 1 - exp(-weight))
|
||||
|
||||
static func dampf(from: float, to: float, weight: float):
|
||||
static func dampf(from: float, to: float, weight: float) -> float:
|
||||
return lerpf(from, to, 1 - exp(-weight))
|
||||
|
||||
static func damp_angle(from: float, to: float, weight: float):
|
||||
static func damp_angle(from: float, to: float, weight: float) -> float:
|
||||
return lerp_angle(from, to, 1 - exp(-weight))
|
||||
|
||||
static func damp_spherical(from: Vector3, to: Vector3, weight: float):
|
||||
static func damp_spherical(from: Vector3, to: Vector3, weight: float) -> Vector3:
|
||||
return from.slerp(to, 1 - exp(-weight))
|
||||
|
||||
static func damp_vector(from: Vector3, to: Vector3, weight: float) -> Vector3:
|
||||
return from.lerp(to, 1 - exp(-weight))
|
||||
|
||||
static func from_to_rotation(from: Vector3, to: Vector3) -> Quaternion:
|
||||
var axis := from.cross(to).normalized()
|
||||
var angle := from.angle_to(to)
|
||||
|
@ -1,5 +1,5 @@
|
||||
class_name Character
|
||||
extends CharacterBody3D
|
||||
extends Node3D
|
||||
|
||||
signal controlled(Controller)
|
||||
|
||||
|
9
client/player/CharacterComponent.gd
Normal file
9
client/player/CharacterComponent.gd
Normal file
@ -0,0 +1,9 @@
|
||||
class_name CharacterComponent
|
||||
extends Node
|
||||
|
||||
var character: Character
|
||||
|
||||
func _enter_tree():
|
||||
assert(owner, "owner not set")
|
||||
assert(owner is Character, "owner must be a character")
|
||||
character = owner
|
@ -7,10 +7,16 @@ var id: String
|
||||
## Components
|
||||
var movement: MovementComponent
|
||||
var state: StateComponent
|
||||
var performance: PerformanceComponent
|
||||
var animation: AnimationComponent
|
||||
var physics: CharacterBody3D
|
||||
|
||||
func _enter_tree():
|
||||
movement = $Movement
|
||||
state = $State
|
||||
performance = $Performance
|
||||
animation = $Animation
|
||||
physics = $Physics
|
||||
|
||||
## Name
|
||||
signal name_changed(new_name: String)
|
||||
|
@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=31 format=3 uid="uid://2lcnu3dy54lx"]
|
||||
[gd_scene load_steps=32 format=3 uid="uid://2lcnu3dy54lx"]
|
||||
|
||||
[ext_resource type="Script" path="res://player/Player.gd" id="1_8gebs"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8j7t4yg7anb0" path="res://assets/female/Female.blend" id="2_8nah6"]
|
||||
@ -26,6 +26,7 @@
|
||||
[ext_resource type="AudioStream" uid="uid://cdywep3dxm0y3" path="res://assets/audio/footsteps/Footsteps-Human-Dirt-07.wav" id="19_vvmo0"]
|
||||
[ext_resource type="AudioStream" uid="uid://bp8pka7qhcetq" path="res://assets/audio/footsteps/Footsteps-Human-Dirt-08.wav" id="20_srjtb"]
|
||||
[ext_resource type="AudioStream" uid="uid://bgdodasvt7ier" path="res://assets/audio/footsteps/Footsteps-Human-Dirt-01.wav" id="21_a8ikg"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5yfm1sektkv" path="res://player/performance/PerformanceComponent.tscn" id="22_iqcgj"]
|
||||
[ext_resource type="PackedScene" uid="uid://sx4ein0bju6m" path="res://player/state/StateComponent.tscn" id="28_0i0of"]
|
||||
[ext_resource type="PackedScene" uid="uid://csidusk3jpq0m" path="res://player/voice/VoiceComponent.tscn" id="28_iolce"]
|
||||
|
||||
@ -58,10 +59,16 @@ stream_8/weight = 1.0
|
||||
stream_9/stream = ExtResource("21_a8ikg")
|
||||
stream_9/weight = 1.0
|
||||
|
||||
[node name="Player" type="CharacterBody3D" groups=["player"]]
|
||||
[node name="Player" type="Node3D"]
|
||||
script = ExtResource("1_8gebs")
|
||||
|
||||
[node name="Physics" type="CharacterBody3D" parent="."]
|
||||
collision_layer = 512
|
||||
collision_mask = 769
|
||||
script = ExtResource("1_8gebs")
|
||||
|
||||
[node name="Collision" type="CollisionShape3D" parent="Physics"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
|
||||
shape = SubResource("CapsuleShape3D_2f50n")
|
||||
|
||||
[node name="Model" type="Node3D" parent="."]
|
||||
|
||||
@ -97,10 +104,6 @@ bone_idx = 44
|
||||
[node name="Heirloom" parent="Model/Female/Armature/GeneralSkeleton/Weapon" instance=ExtResource("7_u8433")]
|
||||
transform = Transform3D(-4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0, 1, 0.197644, 0, 0)
|
||||
|
||||
[node name="Collision" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
|
||||
shape = SubResource("CapsuleShape3D_2f50n")
|
||||
|
||||
[node name="HUD" parent="." node_paths=PackedStringArray("health") instance=ExtResource("7_fwgtd")]
|
||||
layers = 512
|
||||
visibility_range_end = 50.0
|
||||
@ -129,10 +132,14 @@ footsteps = SubResource("AudioStreamRandomizer_4yj1k")
|
||||
[node name="Health" parent="." instance=ExtResource("2_np5ag")]
|
||||
max_value = 100.0
|
||||
|
||||
[node name="Movement" parent="." instance=ExtResource("8_25qd0")]
|
||||
[node name="Movement" parent="." node_paths=PackedStringArray("body") instance=ExtResource("8_25qd0")]
|
||||
body = NodePath("../Physics")
|
||||
|
||||
[node name="Performance" parent="." node_paths=PackedStringArray("animation") instance=ExtResource("22_iqcgj")]
|
||||
animation = NodePath("../Animation")
|
||||
|
||||
[node name="Rotation" parent="." node_paths=PackedStringArray("root") instance=ExtResource("9_agxqu")]
|
||||
root = NodePath("..")
|
||||
root = NodePath("../Model")
|
||||
|
||||
[node name="Skills" parent="." instance=ExtResource("14_6idcf")]
|
||||
skills = Array[Resource("res://skill/Skill.gd")]([ExtResource("2_x58e1"), ExtResource("3_l76ly"), null, ExtResource("5_pnues")])
|
||||
|
@ -1,16 +1,27 @@
|
||||
class_name AnimationComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
@export var skip_frames: int = 0
|
||||
|
||||
var animations: AnimationPlayer
|
||||
var state: StateComponent
|
||||
var accumulated_delta: float
|
||||
var skipped_frames: int
|
||||
|
||||
func _ready():
|
||||
state = owner.get_node("State")
|
||||
state = character.get_node("State")
|
||||
state.transitioned.connect(on_transition)
|
||||
animations = %AnimationPlayer
|
||||
|
||||
func _process(delta):
|
||||
animations.advance(delta)
|
||||
accumulated_delta += delta
|
||||
|
||||
if skipped_frames >= skip_frames:
|
||||
animations.advance(accumulated_delta)
|
||||
accumulated_delta = 0
|
||||
skipped_frames = 0
|
||||
else:
|
||||
skipped_frames += 1
|
||||
|
||||
func on_transition(_from: StateComponent.State, to: StateComponent.State):
|
||||
match to:
|
||||
|
@ -1,22 +1,25 @@
|
||||
class_name ProxyController
|
||||
extends Controller
|
||||
|
||||
@export var interpolation_speed := 5.0
|
||||
@export var interpolation_speed := 20.0
|
||||
|
||||
var player: Player
|
||||
var movement: MovementComponent
|
||||
var server_position: Vector3
|
||||
|
||||
func _init(new_player: Player):
|
||||
player = new_player
|
||||
movement = player.movement
|
||||
name = "Controller"
|
||||
process_physics_priority = 1
|
||||
|
||||
func _ready():
|
||||
server_position = player.position
|
||||
|
||||
func _physics_process(delta: float):
|
||||
if absf(server_position.x - player.position.x) < 0.001 && absf(server_position.z - player.position.z) < 0.001:
|
||||
if absf(server_position.x - movement.physics_position.x) < 0.001 && absf(server_position.z - movement.physics_position.z) < 0.001:
|
||||
return
|
||||
|
||||
var time := interpolation_speed * delta
|
||||
player.position.x = Math.dampf(player.position.x, server_position.x, time)
|
||||
player.position.z = Math.dampf(player.position.z, server_position.z, time)
|
||||
movement.physics_position.x = Math.dampf(movement.physics_position.x, server_position.x, time)
|
||||
movement.physics_position.z = Math.dampf(movement.physics_position.z, server_position.z, time)
|
@ -1,24 +1,30 @@
|
||||
class_name MovementComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
static var ticks_per_second := ProjectSettings.get_setting("physics/common/physics_ticks_per_second") as float
|
||||
static var gravity := ProjectSettings.get_setting("physics/3d/default_gravity") as float
|
||||
|
||||
@export var body: CharacterBody3D
|
||||
@export var move_speed := 4.5
|
||||
@export var jump_velocity := 4.5
|
||||
@export var deceleration_speed := 20
|
||||
|
||||
var body: Character
|
||||
var direction: Vector3
|
||||
var gravity: float
|
||||
var physics_position: Vector3
|
||||
|
||||
func _ready():
|
||||
gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
body = owner as Character
|
||||
body.controlled.connect(on_controlled)
|
||||
physics_position = character.position
|
||||
character.controlled.connect(on_controlled)
|
||||
|
||||
func on_controlled(controller: Controller):
|
||||
controller.direction_changed.connect(on_direction_changed)
|
||||
controller.jumped.connect(jump)
|
||||
func _process(delta: float):
|
||||
character.position = Math.damp_vector(character.position, physics_position, ticks_per_second * delta)
|
||||
|
||||
func _physics_process(delta):
|
||||
func _physics_process(delta: float):
|
||||
begin_physics()
|
||||
move(delta)
|
||||
end_physics()
|
||||
|
||||
func move(delta: float):
|
||||
if direction:
|
||||
body.velocity.x = direction.x * move_speed
|
||||
body.velocity.z = direction.z * move_speed
|
||||
@ -34,11 +40,22 @@ func _physics_process(delta):
|
||||
|
||||
body.move_and_slide()
|
||||
|
||||
func on_direction_changed(new_direction: Vector3):
|
||||
direction = new_direction
|
||||
func begin_physics():
|
||||
body.global_position = physics_position
|
||||
|
||||
func end_physics():
|
||||
physics_position = body.global_position
|
||||
body.position = Vector3.ZERO
|
||||
|
||||
func can_jump() -> bool:
|
||||
return body.is_on_floor()
|
||||
|
||||
func jump():
|
||||
body.velocity.y = jump_velocity
|
||||
|
||||
func on_controlled(controller: Controller):
|
||||
controller.direction_changed.connect(on_direction_changed)
|
||||
controller.jumped.connect(jump)
|
||||
|
||||
func on_direction_changed(new_direction: Vector3):
|
||||
direction = new_direction
|
||||
|
72
client/player/performance/PerformanceComponent.gd
Normal file
72
client/player/performance/PerformanceComponent.gd
Normal file
@ -0,0 +1,72 @@
|
||||
class_name PerformanceComponent
|
||||
extends VisibleOnScreenNotifier3D
|
||||
|
||||
const SKIP_FRAMES_INVISIBLE := 16
|
||||
const SKIP_FRAMES_VISIBLE := 8
|
||||
|
||||
static var quality_budget: Array[int] = [
|
||||
10, # 0 skipped frames
|
||||
10, # 1 skipped frame
|
||||
10, # 2 skipped frames
|
||||
10, # 3 skipped frames
|
||||
10, # 4 skipped frames
|
||||
10, # 5 skipped frames
|
||||
10, # 6 skipped frames
|
||||
10, # 7 skipped frames
|
||||
]
|
||||
|
||||
@export var animation: AnimationComponent
|
||||
|
||||
var camera_distance_squared: float
|
||||
var on_screen: bool
|
||||
var skip_frames_visible: int
|
||||
|
||||
func _ready():
|
||||
assert(animation)
|
||||
screen_entered.connect(on_screen_entered)
|
||||
screen_exited.connect(on_screen_exited)
|
||||
|
||||
func on_screen_entered():
|
||||
on_screen = true
|
||||
animation.skip_frames = skip_frames_visible
|
||||
|
||||
func on_screen_exited():
|
||||
on_screen = false
|
||||
animation.skip_frames = SKIP_FRAMES_INVISIBLE
|
||||
|
||||
static func update_all_animations():
|
||||
var visible_players: Array[Player] = []
|
||||
|
||||
for player in Global.players.id_to_player.values():
|
||||
player.performance.camera_distance_squared = player.global_position.distance_squared_to(Global.camera.global_position)
|
||||
|
||||
if player.performance.on_screen:
|
||||
visible_players.append(player)
|
||||
else:
|
||||
player.performance.animation.skip_frames = SKIP_FRAMES_INVISIBLE
|
||||
|
||||
if Global.player:
|
||||
Global.player.performance.camera_distance_squared = 0
|
||||
|
||||
visible_players.sort_custom(PerformanceComponent.distance_sort)
|
||||
|
||||
var skip_frames := 0
|
||||
var count := 0
|
||||
|
||||
for player in visible_players:
|
||||
if skip_frames >= quality_budget.size():
|
||||
player.performance.skip_frames_visible = SKIP_FRAMES_VISIBLE
|
||||
player.animation.skip_frames = SKIP_FRAMES_VISIBLE
|
||||
continue
|
||||
|
||||
player.performance.skip_frames_visible = skip_frames
|
||||
player.animation.skip_frames = skip_frames
|
||||
count += 1
|
||||
|
||||
if count >= quality_budget[skip_frames]:
|
||||
skip_frames += 1
|
||||
count = 0
|
||||
|
||||
static func distance_sort(a: Player, b: Player) -> bool:
|
||||
return a.performance.camera_distance_squared < b.performance.camera_distance_squared
|
||||
|
7
client/player/performance/PerformanceComponent.tscn
Normal file
7
client/player/performance/PerformanceComponent.tscn
Normal file
@ -0,0 +1,7 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b5yfm1sektkv"]
|
||||
|
||||
[ext_resource type="Script" path="res://player/performance/PerformanceComponent.gd" id="1_vp0dv"]
|
||||
|
||||
[node name="Performance" type="VisibleOnScreenNotifier3D"]
|
||||
aabb = AABB(-1, 0, -1, 2, 2, 2)
|
||||
script = ExtResource("1_vp0dv")
|
@ -1,5 +1,5 @@
|
||||
class_name RotationComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
@export var root: Node3D
|
||||
@export var rotation_speed: float = 15.0
|
||||
@ -8,8 +8,9 @@ var direction: Vector3
|
||||
var angle: float
|
||||
|
||||
func _ready():
|
||||
assert(root, "Rotation root needs to be set")
|
||||
(owner as Character).controlled.connect(on_controlled)
|
||||
assert(root, "rotation root needs to be set")
|
||||
assert(rotation_speed > 0, "rotation speed must be greater than zero")
|
||||
character.controlled.connect(on_controlled)
|
||||
|
||||
func _process(delta):
|
||||
if absf(angle_difference(root.rotation.y, angle)) < 0.001:
|
||||
|
@ -1,12 +1,9 @@
|
||||
class_name SkillsComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
@export var skills: Array[Skill]
|
||||
|
||||
var character: Character
|
||||
|
||||
func _ready():
|
||||
character = owner
|
||||
character.controlled.connect(on_controlled)
|
||||
|
||||
func on_controlled(controller: Controller):
|
||||
@ -25,4 +22,4 @@ func use_skill(slot: int):
|
||||
return
|
||||
|
||||
var scene := skill.scene.instantiate()
|
||||
owner.add_child(scene)
|
||||
character.add_child(scene)
|
||||
|
@ -1,5 +1,5 @@
|
||||
class_name StateComponent
|
||||
extends Node
|
||||
extends CharacterComponent
|
||||
|
||||
enum State {
|
||||
None,
|
||||
@ -17,7 +17,7 @@ var _current: State
|
||||
|
||||
func _ready():
|
||||
current = State.Idle
|
||||
movement = owner.get_node("Movement")
|
||||
movement = character.get_node("Movement")
|
||||
|
||||
func _process(_delta):
|
||||
if current == State.Skill:
|
||||
|
@ -25,4 +25,6 @@ func on_focus_exited():
|
||||
func on_text_submitted(message: String):
|
||||
text = ""
|
||||
release_focus()
|
||||
(owner as Chat).message_submitted.emit(message)
|
||||
|
||||
var chat := owner as Chat
|
||||
chat.message_submitted.emit(message)
|
||||
|
@ -4,6 +4,6 @@ extends Label
|
||||
func _ready():
|
||||
text = get_parent().name + ":"
|
||||
|
||||
func _get_configuration_warnings():
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
text = get_parent().name + ":"
|
||||
return []
|
||||
|
@ -4,4 +4,4 @@ func _process(_delta):
|
||||
if Global.player == null:
|
||||
return
|
||||
|
||||
text = str(Global.player.velocity)
|
||||
text = str(Global.player.physics.velocity)
|
||||
|
@ -1,27 +1,33 @@
|
||||
class_name PlayerManager
|
||||
extends Node3D
|
||||
|
||||
var players = {}
|
||||
var id_to_player = {}
|
||||
|
||||
func _ready():
|
||||
Global.players = self
|
||||
|
||||
var timer := %UpdatePlayers as Timer
|
||||
timer.timeout.connect(tick)
|
||||
|
||||
func tick():
|
||||
PerformanceComponent.update_all_animations()
|
||||
|
||||
func add(player: Player):
|
||||
if has(player.id):
|
||||
return
|
||||
|
||||
add_child(player)
|
||||
players[player.id] = player
|
||||
id_to_player[player.id] = player
|
||||
|
||||
func get_player(id: String) -> Player:
|
||||
return players[id] as Player
|
||||
return id_to_player[id] as Player
|
||||
|
||||
func has(id: String) -> bool:
|
||||
return players.has(id)
|
||||
return id_to_player.has(id)
|
||||
|
||||
func remove(id: String):
|
||||
if !players.has(id):
|
||||
if !id_to_player.has(id):
|
||||
return
|
||||
|
||||
players[id].queue_free()
|
||||
players.erase(id)
|
||||
id_to_player[id].queue_free()
|
||||
id_to_player.erase(id)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
@ -49,7 +50,8 @@ func udpClient(wg *sync.WaitGroup, id int) {
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
loginRequest := [2]string{fmt.Sprintf("user%d", id+1), sha256Text("password")}
|
||||
username := fmt.Sprintf("user%d", id+1)
|
||||
loginRequest := [2]string{username, sha256Text("password")}
|
||||
data, err := json.Marshal(loginRequest)
|
||||
|
||||
if err != nil {
|
||||
@ -69,6 +71,10 @@ func udpClient(wg *sync.WaitGroup, id int) {
|
||||
return
|
||||
}
|
||||
|
||||
if rand.Float32() > 0.5 {
|
||||
conn.Write([]byte{13})
|
||||
}
|
||||
|
||||
time.Sleep(*sleepTime)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user