-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSERVER_window_main.gd
1938 lines (1546 loc) · 59.1 KB
/
SERVER_window_main.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
extends Node2D
# Developed in: Godot 4.2.1
# License: BSD 3-Clause (https://opensource.org/licenses/BSD-3-Clause)
# Author: g023 - https://github.com/g023
# Version: 0.01a
# Description: A text based MUD (Multi User Dungeon) game in Godot GDScript
# Example starts up servers on ports 3500 and 3600
# use a telnet client to login. No security is implemented yet, so careful about making this public.
# This is a work in progress and is not complete. It may be a good starting point for a text based MUD game.
# implemented:
# - telnet server
# - state handler
# - game loop for handling ai and other game upkeep
# - basic mob loading
# - items that exist in 2 rooms at once (eg 2 way doors)
# - portals
# - right now world is linearly created at start of game using code.
# - everything routed through a params and flags object for ease of saving and loading later (saving/loading not implemented yet)
# - commands:
# - say: tell players something in room
# - tell: tell an ingame player something
# - shout: tell everyone something
# - look: look at room, or look at object
# - get: get an object (get from room, or get from object)
# - drop: drop an object
# - put: put an object in another object
# - enter: enter a portal or door
# - who: list players in game
# - stat: show player stats (stat mp,hp,mv for specific)
# - quit: quit the game
# - n,e,s,w,ne,se,sw,nw: move in a direction (also handles long form)
# TODO:
# - Ai integration for editor using local and remote LLM Godot class for tying into chatgpt and lmstudio
# - show inventory
# - split out code into separate files
# - clean out redundant code that is not used and has been drifting in the code since 2020
# - basic fight engine
# - saves and loads
# - more gui control
# BEGIN :: Multidimensional array with 2 keys and a value
# Multidimensional array with 2 keys and a value
# Godot version of my plist class that was made for my C++ mud
# encrypted file saving and loading
class TwoKeyArray:
var data = {}
# Initialize the array with default values
func _init():
data = {}
func _reset():
self.data = {}
# Set a value in the array
func set_value(key1, key2, value):
if not data.has(key1):
data[key1] = {}
data[key1][key2] = value
# Get a value from the array
func get_value(key1, key2):
if data.has(key1) and data[key1].has(key2):
return data[key1][key2]
else:
return null
# Remove a value from the array
func remove_value(key1, key2):
if data.has(key1) and data[key1].has(key2):
data[key1].erase(key2)
if data[key1].size() == 0:
data.erase(key1)
# Check if a key exists in the array
func has_key(key1, key2):
return data.has(key1) and data[key1].has(key2)
# Get all keys from the array
func get_keys():
var keys = []
for key1 in data.keys():
for key2 in data[key1].keys():
keys.append([key1, key2])
return keys
func get_subkeys(key1):
var subkeys = []
if data.has(key1):
for key2 in data[key1].keys():
subkeys.append(key2)
return subkeys
func get_subkey(key1):
if data.has(key1):
return data[key1]
func save_str():
return JSON.stringify(self.data)
func load_str(the_str):
self.data = JSON.parse_string(the_str)
# Save function to serialize data to a file
func _save(filename, key):
# FileAccess open_encrypted_with_pass ( String path, ModeFlags mode_flags, String pass ) static
#var file = FileAccess.open(filename, FileAccess.WRITE)
var file = FileAccess.open_encrypted_with_pass(filename, FileAccess.WRITE, key)
file.store_string(JSON.stringify(self.data))
# Load function to deserialize data from a file
func _load(filename, key):
# FileAccess open_encrypted_with_pass ( String path, ModeFlags mode_flags, String pass ) static
#var file = FileAccess.open(filename, FileAccess.READ)
var file = FileAccess.open_encrypted_with_pass(filename, FileAccess.READ, key)
self.data = JSON.parse_string(file.get_as_text())
## END :: 2 key array class
## TEST:
func test_2key():
# Create an instance of the TwoKeyArray class
var two_key_array = TwoKeyArray.new()
# Set values (first two values are unique, 3rd is the value)
two_key_array.set_value("key1", "key2", "value")
two_key_array.set_value("another_key1", "another_key2", "another_value")
two_key_array.set_value("animal", "pet", "dog")
two_key_array.set_value("animal", "pet", "cat") # overwrites dog with cat
two_key_array._save("myfile.bin","donkeytacowakkawakka")
# Get values
var value = two_key_array.get_value("key1", "key2")
print("Value:", value) # Output: Value: value
# Remove a value
two_key_array.remove_value("key1", "key2")
# Check if a key exists
var has_key = two_key_array.has_key("key1", "key2")
print("Has key:", has_key) # Output: Has key: false
# Get all keys
var keys = two_key_array.get_keys()
print("Keys:", keys) # Output: Keys: [["another_key1", "another_key2"]]
two_key_array._reset()
two_key_array._load("myfile.bin","donkeytacowakkawakka")
print("Keys:", two_key_array.get_keys())
## END TEST.
### ### ###
# same as TwoKeyArray except value is a boolean
class TwoKeyFlags:
var two_key_array = TwoKeyArray.new()
func save_str():
return self.two_key_array.save_str()
func load_str(the_str):
self.two_key_array.load_str(the_str)
func add_flag(flag_name_a, flag_name_b):
two_key_array.set_value(flag_name_a, flag_name_b, "1")
# TODO: add a function to add multiple flags to a flag_name_a seperated by a delim
# eg) .add_flags("can", "wield,hold", ",");
# eg) .add_flags ("keywords", "trash,trash,can,trashcan,garbage", ",");
func add_flags(flag_name_a, flags_str, delim):
var flags = flags_str.split(delim)
for flag_name_b in flags:
two_key_array.set_value(flag_name_a, flag_name_b, "1")
func is_flag(flag_name_a, flag_name_b):
return two_key_array.has_key(flag_name_a, flag_name_b)
func rem_flag(flag_name_a, flag_name_b):
two_key_array.remove_value(flag_name_a, flag_name_b)
func get_flags(key1):
return self.two_key_array.get_subkeys(key1)
pass
## END :: 2 key array class
## TEST:
func test_2keyflags():
var tk_flags = TwoKeyFlags.new()
# Example usage:
tk_flags.add_flags("can", "wield,hold", ",")
tk_flags.add_flags("keywords", "trash,trash,can,trashcan,garbage", ",")
print(tk_flags.two_key_array.get_keys())
print("keywords:",tk_flags.two_key_array.get_subkeys('keywords'))
pass
### ### ###
class inventory:
var game_objects = []
func new_obj():
self.game_objs.append(game_object.new())
return self.game_objs.back()
func get_save_str():
var safe_str_p = ""
var safe_str_f = ""
for key in self.game_objects:
var val = self.game_objects[key]
# poke poke... do something
# two_key_array._save("myfile.bin","donkeytacowakkawakka")
safe_str_p = safe_str_p + val.params
safe_str_f = safe_str_f + val.flags
var r = [safe_str_p,safe_str_f]
return r # get_save_str
pass # inventory
class game_object:
var params = TwoKeyArray.new()
var flags = TwoKeyFlags.new()
#var inventory = null # right now holding off on this part
func _init():
# class constructor
self.params.set_value("info", "uid", self.generate_unique_id())
# self.inventory = inventory.new() # give this object an inventory
pass # constructor _init()
func generate_unique_id():
# Get current timestamp as string
var timestamp_str := Time.get_ticks_msec()
# Generate a random number
var random_num := randi()
# Concatenate timestamp and random number to form the unique ID
var unique_id := str(timestamp_str) + "_" + str(random_num)
return unique_id
pass # class game_object
# TRACK GLOBALS (pass to class in _ready():)
var g_items_proto = [] # stores the templates for the items
var g_items = []
var g_rooms = []
var g_areas = []
var g_mobs_proto = []
var g_mobs = []
var g_world = game_object.new()
func init_globals():
### --- BEGIN: AREA -->
var avnum
var cur_area
# order of creation:
# 1) create area
# 2) create items prototypes
# 3) create mobs prototypes
# 4) create rooms
# 5) load items into rooms
# 6) load items into items
# 7) load mobs into rooms
# 8) load items into mobs
# 9) equip mobs with their items
avnum = "a9100"
g_areas.append(game_object.new()) # You get an object... you get an object... everyone gets an object!
cur_area = g_areas.back()
print(cur_area)
### BEGIN ITEMS ###
var ivnum
var cur_proto
ivnum = "i9100"
g_items_proto.append(game_object.new()) # You get an object... you get an object... everyone gets an object!
cur_proto = g_items_proto[-1]
cur_proto.params.set_value("info","vnum", ivnum)
cur_proto.params.set_value("area","vnum", avnum)
cur_proto.params.set_value("info","title", "A Thin Glass Rod")
cur_proto.params.set_value("info","room","A slender transparent rod shimmers.")
cur_proto.params.set_value("info","description",
"There is really nothing special about this thin glass rod. It really looks cheap and inexpensive. It may be useful\r\n"+
"for something.\r\n"
)
cur_proto.flags.add_flags("can","take,hold",",")
cur_proto.flags.add_flags("keywords","thin,glass,rod,glass rod,thin rod,thin glass rod",",")
# cur_proto.params.set_value("owner","uid","") # get from "info", "uid" on every object
ivnum = "i9101"
g_items_proto.append(game_object.new()) # You get an object... you get an object... everyone gets an object!
cur_proto = g_items_proto[-1]
cur_proto.params.set_value("info","vnum", ivnum)
cur_proto.params.set_value("area","vnum", avnum)
cur_proto.params.set_value("info","title", "A Rusty Iron Key")
cur_proto.params.set_value("info","room","A small rusty iron key lies here.")
cur_proto.params.set_value("info","description",
"This small rusty iron key looks like it has been here for a long time. It may be useful for something.\r\n"
)
cur_proto.flags.add_flags("can","take,hold",",")
cur_proto.flags.add_flags("keywords","rusty,iron,key,iron key,rusty key,rusty iron key",",")
# cur_proto.params.set_value("owner","uid","") # get from "info", "uid" on every object
ivnum = "i9113"
g_items_proto.append(game_object.new()) # You get an object... you get an object... everyone gets an object!
cur_proto = g_items_proto[-1]
cur_proto.params.set_value("info","vnum", ivnum)
cur_proto.params.set_value("area","vnum", avnum)
cur_proto.params.set_value("info","title", "An old oak door")
cur_proto.params.set_value("info","room", "An old oak door is here.")
cur_proto.params.set_value("info","description",
"This old oak door is very sturdy and looks like it has been here for a long time.\r\n"
)
cur_proto.flags.add_flags("is","2way",",")
cur_proto.flags.add_flags("can","lock,unlock,open,close,enter",",")
cur_proto.flags.add_flags("keywords","old,oak,door,old door,oak door,old oak door",",")
# cur_proto.params.set_value("owner","uid","") # get from "info", "uid" on every object
# cur_proto.params.set_value("to","uid","") # portal info for attached room
### END ITEMS ###
### BEGIN MOBS ###
var mvnum
var cur_mob
mvnum = "m9100"
g_mobs_proto.append(game_object.new())
cur_mob = g_mobs_proto[-1]
cur_mob.params.set_value("info","vnum", mvnum)
cur_mob.params.set_value("area","vnum", avnum)
cur_mob.params.set_value("info","title", "A Small Dinosaur")
cur_mob.params.set_value("info","description", "A small dinosaur is here.")
# keywords
cur_mob.flags.add_flags("keywords","small,dinosaur,small dinosaur",",")
mvnum = "m9101"
g_mobs_proto.append(game_object.new())
cur_mob = g_mobs_proto[-1]
cur_mob.params.set_value("info","vnum", mvnum)
cur_mob.params.set_value("area","vnum", avnum)
cur_mob.params.set_value("info","title", "A Large Dinosaur")
cur_mob.params.set_value("info","description", "A large dinosaur is here.")
### END MOBS ###
### BEGIN ROOMS ###
var rvnum
var cur_room
cur_area.params.set_value("info","vnum",avnum)
rvnum = "r9100"
g_world.params.set_value("start","vnum",rvnum)
g_rooms.append(game_object.new())
cur_room = g_rooms[-1]
cur_room.params.set_value("info","vnum", rvnum)
cur_room.params.set_value("area","vnum", avnum)
cur_room.params.set_value("info","title", "Entrance to a Godot MUD")
cur_room.params.set_value("info","description", "A certain calm befalls this place. The blades of grass bend over rhythmically with the pulse of a gentle breeze.\r\n"+
"You feel safe here, as if nothing in the world matters, save the calm serenity of peace. The sound of birds singing\r\n"+
"in the trees and the swaying song of the leaves rustling together in the wind makes you feel a tingly sensation of\r\n"+
"comfort and warmth."
)
cur_room.params.set_value("room","type","outdoors")
cur_room.params.set_value("exit","n","r9101") # use short form
cur_room.params.set_value("exit_desc","n","A gentle breeze flows at you.")
### create an object in room ###
# find the object in the prototypes array matching a vnum and tie its owner uid to current room
# make a load_proto(proto_vnum, global_items_arr, owner_uid)
# var g_items_proto = []
var load_obj = "i9100"
var last_item = null
for proto in g_items_proto:
if proto.params.get_value("info","vnum") == load_obj:
#var new_item = game_object.new()
g_items.append(game_object.new())
var new_item = g_items[-1]
# deep copy
var params_str = proto.params.save_str()
var flags_str = proto.flags.save_str()
print("p:",params_str)
print("f:",flags_str)
new_item.params.load_str(params_str)
new_item.flags.load_str(flags_str)
#new_item.params.load_str(proto.params.save_str)
#new_item.flags.load_str(proto.flags.save_str)
# last_item = g_items[-1]
# re-uid it
# self.params.set_value("info", "uid", self.generate_unique_id())
new_item.params.set_value("info","uid",new_item.generate_unique_id())
# set owner uid to room
new_item.params.set_value("owner","uid",cur_room.params.get_value("info","uid"))
# set as a portal
new_item.params.set_value("to","rvnum","r9102")
last_item = new_item
break
### end create an object in room ###
### create an object in object (load in object i9101 to last one) ###
load_obj = "i9101"
for proto in g_items_proto:
if proto.params.get_value("info","vnum") == load_obj:
#var new_item = game_object.new()
g_items.append(game_object.new())
var new_item = g_items[-1]
# deep copy - first get the serialized arrays
var params_str = proto.params.save_str()
var flags_str = proto.flags.save_str()
print("p:",params_str)
print("f:",flags_str)
# ... now write them to new object
new_item.params.load_str(params_str)
new_item.flags.load_str(flags_str)
# last_item = g_items[-1]
# re-uid it
new_item.params.set_value("info","uid",new_item.generate_unique_id())
# set owner uid to last_item's uid
new_item.params.set_value("owner","uid",last_item.params.get_value("info","uid"))
break
### end create an object in object ###
### create an object in room ###
# find the object in the prototypes array matching a vnum and tie its owner uid to current room
# make a load_proto(proto_vnum, global_items_arr, owner_uid)
# var g_items_proto = []
load_obj = "i9113"
for proto in g_items_proto:
if proto.params.get_value("info","vnum") == load_obj:
g_items.append(game_object.new())
var new_item = g_items[-1]
var params_str = proto.params.save_str()
var flags_str = proto.flags.save_str()
new_item.params.load_str(params_str)
new_item.flags.load_str(flags_str)
new_item.params.set_value("info","uid",new_item.generate_unique_id())
new_item.params.set_value("owner","uid",cur_room.params.get_value("info","uid"))
last_item = new_item
# done loading new proto.. now since this is a door, lets set its remote door
# .params.set_value("to","uid","") # portal info for attached room
# get room at vnum r9102
# set link vnum to r9102
new_item.params.set_value("to","rvnum","r9102")
# when a user picks up, set a flag whether it was linked door that was picked up, or main.
# store destination vnum to be retrieved when exit is dropped in a room
### end create an object in room ###
rvnum = "r9101"
g_rooms.append(game_object.new())
cur_room = g_rooms[-1]
cur_room.params.set_value("info","vnum", rvnum)
cur_room.params.set_value("area","vnum", avnum)
cur_room.params.set_value("info","title", "A Sandy Beach")
# a beach with interesting features. Usually the room contains a dinosaur and a treasure chest with a treasure map.
cur_room.params.set_value("info","description", ""
+"The sun blazes brilliantly in the clear blue sky, casting a golden glow on the expansive, pristine beach. The sand, \r\n"
+"soft and warm, seems to dance under the sunlight. The rhythmic symphony of waves crashing onto the shore creates an \r\n"
+"exhilarating soundtrack to this adventure. The water, a mesmerizing shade of blue, beckons enticingly. A few clouds dot \r\n"
+"the sky, like cotton candy on a canvas of blue. The beach, untouched by trash or debris, is a testament to the \r\n"
+"unspoiled beauty of nature. Every element here promises an exciting and unforgettable adventure."
)
cur_room.params.set_value("room","type","outdoors")
cur_room.params.set_value("exit","n","r9102") # use short form
cur_room.params.set_value("exit_desc","n","A gentle breeze flows at you.")
cur_room.params.set_value("exit","s","r9100") # use short form
cur_room.params.set_value("exit_desc","s","A gentle breeze flows at you.")
# load mobs m9100 and m9101 into room
var load_mob = ""
# find the object in the prototypes array matching a vnum and tie its owner uid to current room
# make a load_proto(proto_vnum, global_mobs_arr, owner_uid)
# var g_mobs_proto = []
load_mob = "m9100"
for proto in g_mobs_proto:
if proto.params.get_value("info","vnum") == load_mob:
print("loading matching mob in: ",rvnum)
g_mobs.append(game_object.new())
var new_mob = g_mobs[-1]
var params_str = proto.params.save_str()
var flags_str = proto.flags.save_str()
new_mob.params.load_str(params_str)
new_mob.flags.load_str(flags_str)
new_mob.params.set_value("info","uid",new_mob.generate_unique_id())
new_mob.params.set_value("vnum","cur",rvnum)
# end for
# ---
rvnum = "r9102"
g_rooms.append(game_object.new())
cur_room = g_rooms[-1]
cur_room.params.set_value("info","vnum", rvnum)
cur_room.params.set_value("area","vnum", avnum)
cur_room.params.set_value("info","title", "A Dead End")
cur_room.params.set_value("info","description", ""+
"Well this place isn't really described very well now is it? Maybe it's because this is just a test room, and getting\r\n"
+"too crazy now only makes building it harder."
)
cur_room.params.set_value("room","type","indoors")
cur_room.params.set_value("exit","s","r9101") # use short form
cur_room.params.set_value("exit_desc","s","A gentle breeze flows at you.")
### END ROOMS ###
pass
# http://ascii-table.com/ansi-escape-sequences-vt-100.php
#http://ascii-table.com/documents/vt100/chapter3.php#S3.3.3
class telnet_app:
var servers = []
var users = []
var world_arr = []
var time_ms_last_data_check = 0
var delay_ms_data_check = 60
func _init(world_arr):
pass
func welcome(user):
pass
func telnet_echo_off(user):
pass
func telnet_echo_on(user):
pass
func telnet_clrscr(user):
self.send(user, "\u001B[2J")
func telnet_clrln(user):
self.send(user, "\u001B[2K")
func send(user, message):
self.send_nr(user, message + "\r\n")
#self.prompt(user)
func send_nr(user, message): # no return
var client = user.client
client.put_string("\u001B[2K\r" + message)
func send_all(message):
for u in self.users:
var index = self.users.find(u)
var client = u.client
if client.is_connected_to_host():
self.send(u, message)
else:
print("client disconnected")
func send_all_except(except_user, message):
var index_except = self.users.find(except_user) # find by index
for u in self.users:
var index = self.users.find(u)
if index != index_except:
var client = u.client
if client.is_connected_to_host():
# client.put_string(message)
self.send(u, message)
self.prompt(u) # prompting because interrupting other users terminals
else:
print("client disconnected")
func hide_input(user):
var client = user.client
client.put_string("\u001B[2K\r")
func show_input(user):
var client = user.client
client.put_string("\u001B[2K\r")
self.prompt(user)
func prompt(user): # overload
pass
func check_for_command(user):
var client = user.client
var index = users.find( user )
var buf = user.buf
if(buf.find("\n") > -1): # incoming command from client
var command_str = buf.left( buf.find("\n") ).strip_edges()
self.users[index].buf = self.users[index].buf.right( buf.find("\n") + 2).strip_edges()
# debug output in console
print("command found: [" + command_str + "] ")
print("new buffer : [" + users[index].buf + "] ")
self.users[index].buf = "" # wipe buffer
self.handle_command_string(user, command_str)
func check_for_command_users():
for u in self.users:
var index = self.users.find(u)
self.check_for_command(u)
func handle_command_string(user, command_str):
var client = user.client
var index = self.users.find( user )
func create_server(port):
var svr_obj = {}
svr_obj.server = TCPServer.new()
svr_obj.port = port
if svr_obj.server.listen(svr_obj.port) == 0:
print("server started:" + str(port) )
self.servers.append(svr_obj)
func check_for_new_users():
for svr_obj in self.servers:
if svr_obj.server.is_connection_available():
self.create_new_user(svr_obj)
func create_new_user(svr_obj):
var server = svr_obj.server
var port = svr_obj.port
var client = server.take_connection()
var cli_obj = {}
cli_obj.server = server
cli_obj.port = port
cli_obj.client = client
cli_obj.ip = client.get_connected_host()
cli_obj.buf = ""
print("client ip:" + str(cli_obj.ip))
# Disables Nagle’s algorithm to improve latency for small packets
client.set_no_delay(true)
self.users.append(cli_obj)
var last_index = self.users.size() - 1
var user = self.users[last_index]
self.welcome(user)
func check_for_data():
var cur_msecs = Time.get_ticks_msec()
if cur_msecs - time_ms_last_data_check > delay_ms_data_check:
for u in self.users:
var index = self.users.find(u)
var client = u.client
# if client.is_connected_to_host():
if client.get_status() == 2:
if client.get_available_bytes() > 0:
var buf = client.get_partial_data(1024)
buf = buf[1].get_string_from_ascii()
self.users[index].buf += buf
print ("new data in buffer from user #" + str(index) + " : [" + self.users[index].buf + "]")
else:
# print("client disconnected")
pass
self.time_ms_last_data_check = cur_msecs
func disconnect_user(user):
var index = self.users.find(user)
var client = user.client
# self.users.remove(index) # godot 4 now uses remove_at
self.users.remove_at(index)
func server_loop():
OS.delay_usec(1000)
self.check_for_new_users()
self.check_for_data()
self.check_for_command_users()
#
#
#
#
#var server_app
#
#
#func _ready():
# server_app = telnet_app.new()
# server_app.create_server(3500)
# server_app.create_server(3600)
#
#
#func _process(delta):
# server_app.server_loop()
#
class game_command_util:
# standalone functions
# TODO: add in process backspaces, one arg, and any other universal functions
func _init():
pass
# class to handle incoming game_commands - used by class game_app
class game_command:
var p # parent
var util = game_command_util.new()
func _init(the_parent):
print("creating game_command object")
self.p = the_parent
pass
# step 1
func process_string(user, command_str):
# print("inside game_command object")
# print("parent" + to_json( self.p.users ))
#
# var i = self.p.users.find(user)
# self.p.users[i].username = "nnnn" # test to see if child object writing properly
pass
# step 2
func process_state(user, command_str):
var index = self.p.users.find(user)
var user_state = self.p.users[index].player.state
pass
# step 3
func process_command(user, command_str):
pass
# end class: handle incoming game_commands
class game_app:
extends telnet_app
#var world = game_world.new()
var globals = {}
var time_ms_last_game_loop = 0
var delay_ms_game_loop = 50000
var time_ms_last_action_loop = 0
var delay_ms_action_loop = 5000
# handle incoming game commands
var gc = game_command.new(self)
func _init(globals_ref):
print("game created")
self.globals = globals_ref
pass
func prompt(user):
# show a prompt to our player based on their state
match(user.player.state):
"login":
self.send_nr(user, "what is your name adventurer? ")
"password":
self.send_nr(user, "what is your password? ")
_: # default in godot is a _
self.send_nr(user, ">> ")
func create_dummy():
# SHOULD BE OBSOLETE!
# make a blank user
var temp_player = {}
temp_player.params = TwoKeyArray.new()
temp_player.flags = TwoKeyFlags.new()
temp_player.inventory = inventory.new()
temp_player.state = "login"
temp_player.in_world = false
print("starting vnum:", self.globals.g_world.params.get_value("start","vnum"))
#
# temp_player.username = ""
# temp_player.password = ""
temp_player.login_time = Time.get_unix_time_from_system()
temp_player.last_access = Time.get_unix_time_from_system()
temp_player.race = "-1"
temp_player.guild = "-1"
temp_player.room = "-1"
temp_player.fighting = "-1"
temp_player.position = "standing"
temp_player.currency = 100
temp_player.stats = {}
temp_player.stats.hp = 20
temp_player.stats.hp_max = 20
temp_player.stats.mv = 20
temp_player.stats.mv_max = 20
temp_player.stats.mp = 20
temp_player.stats.mp_max = 20
temp_player.stats.strength = 12
temp_player.stats.wisdom = 12
temp_player.stats.intelligence = 12
temp_player.stats.constitution = 12
temp_player.stats.charisma = 12
temp_player.stats.dexterity = 12
return temp_player
pass
func generate_unique_id():
# Get current timestamp as string
var timestamp_str := Time.get_ticks_msec()
# Generate a random number
var random_num := randi()
# Concatenate timestamp and random number to form the unique ID
var unique_id := str(timestamp_str) + "_" + str(random_num)
return unique_id
func dummy(the_user):
var p = the_user.params
var f = the_user.flags
var login_time = Time.get_unix_time_from_system()
var last_access = Time.get_unix_time_from_system()
p.set_value('info','uid',self.generate_unique_id())
p.set_value('vnum','cur',self.globals.g_world.params.get_value("start","vnum"))
#p.params.set_value('','',)
p.set_value('time','login',login_time)
p.set_value('time','last_access',last_access)
p.set_value('state','ui','login')
p.set_value('state','pose','standing')
# when finished login states add flag in game
# when fighting create a fighting key and then second key is vnum as a flag
# eg) f.add_flag('fighting','m1234') # allows for multiple enemies
p.set_value('stat','hp','20')
p.set_value('stat','mp','20')
p.set_value('stat','mv','20')
p.set_value('max','hp','20')
p.set_value('max','mp','20')
p.set_value('max','mv','20')
p.set_value('stat','str','12')
p.set_value('stat','wis','12')
p.set_value('stat','int','12')
p.set_value('stat','con','12')
p.set_value('stat','cha','12')
p.set_value('stat','dex','12')
p.set_value('max','str','18')
p.set_value('max','wis','18')
p.set_value('max','int','18')
p.set_value('max','con','18')
p.set_value('max','cha','18')
p.set_value('max','dex','18')
pass # dummy.. use params/flags
func welcome(user):
var index = self.users.find( user )
self.telnet_clrscr(user)
self.telnet_echo_off(user)
self.send(user, "Welcome new user on port:" + str(user.port) )
# self.send_all_except(user, "NEW CONNECTION")
users[index].params = TwoKeyArray.new()
users[index].flags = TwoKeyFlags.new()
users[index].player = self.create_dummy() # should be using the object class # old dummy
print("starting vnum:", self.globals.g_world.params.get_value("start","vnum"))
self.dummy(users[index]) # new dummy
self.prompt(user)
pass
func action_loop():
var cur_ms = Time.get_ticks_msec()
if cur_ms - time_ms_last_action_loop > delay_ms_action_loop:
time_ms_last_action_loop = cur_ms
print("action loop")
pass
func game_loop():
var cur_ms = Time.get_ticks_msec()
if cur_ms - time_ms_last_game_loop > delay_ms_game_loop:
time_ms_last_game_loop = cur_ms
print("game loop")
pass
func is_playername(str_name):
return true
func is_playerpassword(str_pass):
return true
func get_user_by_name(find_username):
for u in self.users:
var i = self.users.find(u) # index in main array
var username = u.username
if(username == find_username):
print("found user:" + username)
return u
return false
func get_users():
# return self.users
# actually just return in game users so has a flag set "in","game"
var ret_users = []
for u in self.users:
var in_world = u.flags.is_flag("in","game")
if in_world:
ret_users.append(u)
return ret_users
func processBackspace(input_string):
# made a gist mar6-2024: https://gist.github.com/g023/3dea8c91cc1c681ae0da4d4ad6d9c548
var result = ""
var stack := []
if input_string.find("\b") == -1:
return input_string
for char in input_string:
if char == "\b":
if stack.size() > 0:
stack.pop_back()
else:
stack.push_back(char)
else:
stack.append(char)
for char in stack: