serverwatcher 3.3__tar.gz → 3.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {serverwatcher-3.3/src/serverwatcher.egg-info → serverwatcher-3.5}/PKG-INFO +1 -1
  2. {serverwatcher-3.3 → serverwatcher-3.5}/pyproject.toml +1 -1
  3. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/__init__.py +2 -1
  4. serverwatcher-3.5/src/serverwatcher/config/__init__.py +9 -0
  5. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/config/watcher.py +4 -0
  6. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/defaultconfigs/global.yaml +1 -1
  7. serverwatcher-3.5/src/serverwatcher/defaultconfigs/watcher.yaml +39 -0
  8. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/schema.py +25 -0
  9. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/watcher.py +27 -41
  10. {serverwatcher-3.3 → serverwatcher-3.5/src/serverwatcher.egg-info}/PKG-INFO +1 -1
  11. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher.egg-info/SOURCES.txt +1 -0
  12. serverwatcher-3.3/src/serverwatcher/defaultconfigs/watcher.yaml +0 -28
  13. {serverwatcher-3.3 → serverwatcher-3.5}/LICENSE +0 -0
  14. {serverwatcher-3.3 → serverwatcher-3.5}/README.md +0 -0
  15. {serverwatcher-3.3 → serverwatcher-3.5}/setup.cfg +0 -0
  16. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/config/global_config.py +0 -0
  17. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/config/messages.py +0 -0
  18. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher/defaultconfigs/messages.yaml +0 -0
  19. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher.egg-info/dependency_links.txt +0 -0
  20. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher.egg-info/requires.txt +0 -0
  21. {serverwatcher-3.3 → serverwatcher-3.5}/src/serverwatcher.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverwatcher
3
- Version: 3.3
3
+ Version: 3.5
4
4
  Summary: A HungerLib-powered Minecraft server automation engine.
5
5
  Author: iFamished
6
6
  License: GPL-3.0
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "serverwatcher"
10
- version = "3.3"
10
+ version = "3.5"
11
11
  description = "A HungerLib-powered Minecraft server automation engine."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -7,7 +7,7 @@ except PackageNotFoundError:
7
7
  __version__ = '0.0.0'
8
8
 
9
9
  from .watcher import ServerWatcher
10
- from .schema import validate_config_schema, validate_messages_schema
10
+ from .schema import validate_config_schema, validate_messages_schema flatten_nested
11
11
 
12
12
 
13
13
  __all__ = [
@@ -15,4 +15,5 @@ __all__ = [
15
15
  'ServerWatcher',
16
16
  'validate_config_schema',
17
17
  'validate_messages_schema',
18
+ 'flatten_nested',
18
19
  ]
@@ -0,0 +1,9 @@
1
+ from .global_config import GlobalConfig
2
+ from .messages import MessagesConfig
3
+ from .watcher import WatcherConfig
4
+
5
+ __all__ = [
6
+ 'GlobalConfig',
7
+ 'MessagesConfig',
8
+ 'WatcherConfig',
9
+ ]
@@ -30,3 +30,7 @@ class WatcherConfig:
30
30
  log_path: str
31
31
  console_backspaces: int
32
32
  timezone: str
33
+
34
+ # New values (will reorder later)
35
+ watch_interval: int
36
+ clear_terminal: bool
@@ -9,7 +9,7 @@ origin:
9
9
  server:
10
10
  name: My SMP
11
11
  server_id: CHANGE_ME
12
- domain: mc.hungersmp.com
12
+ domain: mc.example.com
13
13
  port: 25565
14
14
  rcon_port: 25575
15
15
  rcon_password: password
@@ -0,0 +1,39 @@
1
+ logging:
2
+ logger_name_template: "Server Watcher"
3
+ log_path: "/home/container/logs/"
4
+ timezone: "America/Chicago"
5
+
6
+ terminal_settings:
7
+ console_backspaces: 8
8
+ clear_terminal: True
9
+
10
+ schedules:
11
+ restart_soon_schedule_id: 0 # replace this with real schedule id
12
+ origin_disable_schedule_id: 0 # replace this with real schedule id
13
+
14
+ thresolds:
15
+ ram_threshold: 6
16
+ cpu_threshold: 150
17
+ uptime_hours_threshold: 12
18
+ tps_threshold: 19.5
19
+
20
+ weights:
21
+ weight_restart_soon: 3
22
+ weight_ram: 1
23
+ weight_cpu: 1
24
+ weight_uptime: 1
25
+ weight_tps: 1
26
+ weight_low_uptime: 5
27
+ weight_per_player: 1
28
+
29
+ gaps:
30
+ low_gap_minutes: 120
31
+ high_gap_minutes: 60
32
+
33
+ restart_intervals:
34
+ restart_wait_seconds: 45
35
+ restart_online_timeout: 120
36
+ restart_online_interval: 2
37
+
38
+ watcher_intervals:
39
+ watch_interval: 60
@@ -43,3 +43,28 @@ def validate_messages_schema(raw: Dict[str, Any]) -> list[str]:
43
43
  if "prefix" not in raw:
44
44
  errors.append("[messages] Missing required key: 'prefix'")
45
45
  return errors
46
+
47
+ def flatten_nested(raw: dict) -> dict:
48
+ """
49
+ Recursively flattens a nested YAML dict into a single-level dict.
50
+ Section names are ignored; only leaf keys matter.
51
+ """
52
+ flat = {}
53
+
54
+ def walk(node):
55
+ if isinstance(node, dict):
56
+ for v in node.values():
57
+ walk(v)
58
+ else:
59
+ # ignore non-dict nodes at this level
60
+ pass
61
+
62
+ def collect(node):
63
+ for key, value in node.items():
64
+ if isinstance(value, dict):
65
+ collect(value)
66
+ else:
67
+ flat[key] = value
68
+
69
+ collect(raw)
70
+ return flat
@@ -15,19 +15,13 @@ from hungerlib.addons import (
15
15
  map_to_dataclass,
16
16
  )
17
17
 
18
- # NEW CONFIG DATACLASSES
19
- from serverwatcher.config.global_config import GlobalConfig
20
- from serverwatcher.config.messages import MessagesConfig
21
- from serverwatcher.config.watcher import WatcherConfig
22
-
23
- # BASE DIRECTORY OF THIS PACKAGE
24
- # BASE_DIR = os.path.dirname(__file__) # OLD VERSION, may use later
25
- BASE_DIR = os.getcwd() # NEW VERSION
18
+ from serverwatcher.config import GlobalConfig, MessagesConfig, WatcherConfig
19
+
20
+ # Set directory
21
+ BASE_DIR = os.getcwd()
26
22
  PACKAGE_DIR = os.path.dirname(__file__)
27
23
 
28
- # ---------------------------------------------------------
29
- # Utility: load config or copy default from defaultconfigs/
30
- # ---------------------------------------------------------
24
+ # Load configs
31
25
  def load_or_default(path: str, default_path: str, schema):
32
26
  """
33
27
  Loads YAML from path. If missing, copies default_path → path.
@@ -43,15 +37,14 @@ def load_or_default(path: str, default_path: str, schema):
43
37
  dst.write(src.read())
44
38
 
45
39
  raw = load_yaml(abs_path)
40
+ raw = flatten_nested(raw)
46
41
  return map_to_dataclass(raw, schema)
42
+
47
43
 
48
44
 
49
- # ---------------------------------------------------------
50
- # Main Watcher
51
- # ---------------------------------------------------------
45
+ # Main watcher
52
46
  class ServerWatcher:
53
47
  def __init__(self):
54
- # Load all 3 configs
55
48
  self.global_cfg: GlobalConfig = load_or_default(
56
49
  "config/global.yaml",
57
50
  "defaultconfigs/global.yaml",
@@ -70,7 +63,7 @@ class ServerWatcher:
70
63
  WatcherConfig
71
64
  )
72
65
 
73
- # PANEL
66
+ # panel
74
67
  p = self.global_cfg.panel
75
68
  self.panel = Panel(
76
69
  name=p["name"],
@@ -78,7 +71,7 @@ class ServerWatcher:
78
71
  api_key=p["api_key"],
79
72
  )
80
73
 
81
- # ORIGIN
74
+ # origin
82
75
  o = self.global_cfg.origin
83
76
  self.origin = GenericServer(
84
77
  name="Origin",
@@ -86,7 +79,7 @@ class ServerWatcher:
86
79
  server_id=o["server_id"],
87
80
  )
88
81
 
89
- # SERVER
82
+ # server
90
83
  s = self.global_cfg.server
91
84
  self.server = MinecraftServer(
92
85
  name=s["name"],
@@ -99,7 +92,7 @@ class ServerWatcher:
99
92
  tpsCommand=s["tps_command"],
100
93
  )
101
94
 
102
- # LOGGER (fully configurable)
95
+ # logger
103
96
  logger_name = self.cfg.logger_name_template.format(
104
97
  server_name=s["name"]
105
98
  )
@@ -111,25 +104,19 @@ class ServerWatcher:
111
104
  console_backspaces=self.cfg.console_backspaces,
112
105
  )
113
106
 
114
- # TIMEZONE
107
+ # timezone
115
108
  self.tz = ZoneInfo(self.cfg.timezone)
116
109
 
117
- # -----------------------------------------------------
118
- # Utility: format messages with prefix
119
- # -----------------------------------------------------
110
+ # format messages with prefix
120
111
  def fmt(self, template: str, **kwargs):
121
112
  return template.format(prefix=self.messages.prefix, **kwargs)
122
113
 
123
- # -----------------------------------------------------
124
- # Shutdown
125
- # -----------------------------------------------------
114
+ # simple shutdown
126
115
  def shutdown(self):
127
116
  self.log.info("Shutting down ServerWatcher.")
128
117
  raise SystemExit
129
118
 
130
- # -----------------------------------------------------
131
- # Restart logic
132
- # -----------------------------------------------------
119
+ # restart logic
133
120
  def restart_and_wait(self):
134
121
  self.origin.disableSchedule(self.cfg.restart_soon_schedule_id)
135
122
  self.server.restart()
@@ -152,9 +139,7 @@ class ServerWatcher:
152
139
  else:
153
140
  self.log.error(f"{self.messages.server_failed_restart}")
154
141
 
155
- # -----------------------------------------------------
156
- # Schedule restart
157
- # -----------------------------------------------------
142
+ # schedule restart
158
143
  def schedule_restart(self, minutes):
159
144
  info = snapSchedule(minimumMinutes=minutes)
160
145
  scheduled = info["scheduled"]
@@ -184,9 +169,7 @@ class ServerWatcher:
184
169
  second_callbacks=second_callbacks,
185
170
  )
186
171
 
187
- # -----------------------------------------------------
188
- # Main evaluation logic
189
- # -----------------------------------------------------
172
+ # main evaluation logic
190
173
  def evaluate(self):
191
174
  self.log.info(self.messages.log_start)
192
175
 
@@ -202,7 +185,7 @@ class ServerWatcher:
202
185
  restart_reasons = []
203
186
  no_restart_reasons = []
204
187
 
205
- # PRO-RESTART
188
+ # pro-restart
206
189
  if self.server.getSchedule(self.cfg.restart_soon_schedule_id)["is_active"]:
207
190
  restart_reasons.append(self.messages.reason_restart_soon)
208
191
  pro += self.cfg.weight_restart_soon
@@ -211,7 +194,7 @@ class ServerWatcher:
211
194
  restart_reasons.append(
212
195
  self.fmt(self.messages.reason_ram, ram=snap.ram, threshold=self.cfg.ram_threshold)
213
196
  )
214
- pro += round(snap.ram, 0) - 5
197
+ pro += int(round(snap.ram, 0) - 5)
215
198
 
216
199
  if snap.cpu >= self.cfg.cpu_threshold:
217
200
  restart_reasons.append(
@@ -232,7 +215,7 @@ class ServerWatcher:
232
215
  )
233
216
  pro += self.cfg.weight_tps
234
217
 
235
- # ANTI-RESTART
218
+ # anti-restart
236
219
  if snap.uptime // 60 < 30:
237
220
  no_restart_reasons.append(
238
221
  self.fmt(self.messages.reason_low_uptime, uptime=snap.uptime_formatted)
@@ -247,7 +230,7 @@ class ServerWatcher:
247
230
  )
248
231
  anti += snap.players * self.cfg.weight_per_player
249
232
 
250
- # LOGGING
233
+ # logging
251
234
  for r in restart_reasons:
252
235
  self.log.warn(f"- {r}")
253
236
  for r in no_restart_reasons:
@@ -282,7 +265,10 @@ class ServerWatcher:
282
265
  # Main loop
283
266
  # -----------------------------------------------------
284
267
  def run(self):
285
- clearTerminal()
268
+ if self.cfg.clear_terminal:
269
+ clearTerminal()
286
270
  while True:
271
+ if self.cfg.clear_terminal:
272
+ clearTerminal()
287
273
  self.evaluate()
288
- time.sleep(60)
274
+ time.sleep(self.cfg.watch_interval)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverwatcher
3
- Version: 3.3
3
+ Version: 3.5
4
4
  Summary: A HungerLib-powered Minecraft server automation engine.
5
5
  Author: iFamished
6
6
  License: GPL-3.0
@@ -9,6 +9,7 @@ src/serverwatcher.egg-info/SOURCES.txt
9
9
  src/serverwatcher.egg-info/dependency_links.txt
10
10
  src/serverwatcher.egg-info/requires.txt
11
11
  src/serverwatcher.egg-info/top_level.txt
12
+ src/serverwatcher/config/__init__.py
12
13
  src/serverwatcher/config/global_config.py
13
14
  src/serverwatcher/config/messages.py
14
15
  src/serverwatcher/config/watcher.py
@@ -1,28 +0,0 @@
1
- restart_soon_schedule_id: 13
2
- origin_disable_schedule_id: 11
3
-
4
- ram_threshold: 6
5
- cpu_threshold: 150
6
- uptime_hours_threshold: 12
7
- tps_threshold: 19.5
8
-
9
- weight_restart_soon: 3
10
- weight_ram: 1
11
- weight_cpu: 1
12
- weight_uptime: 1
13
- weight_tps: 1
14
-
15
- weight_low_uptime: 5
16
- weight_per_player: 1
17
-
18
- low_gap_minutes: 120
19
- high_gap_minutes: 60
20
-
21
- restart_wait_seconds: 45
22
- restart_online_timeout: 120
23
- restart_online_interval: 2
24
-
25
- logger_name_template: "Server Watcher"
26
- log_path: "/home/container/logs/"
27
- console_backspaces: 8
28
- timezone: "America/Chicago"
File without changes
File without changes
File without changes