Skip to content

Commit 682493e

Browse files
committed
Initial commit.
Maps can be downloaded from Dynmap using Minecraft coordinates.
0 parents  commit 682493e

31 files changed

Lines changed: 1697 additions & 0 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__pycache__/
2+
.idea/
3+
build/
4+
venv/
5+
dist/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Ryan Bester
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Minecraft Map Tools
2+
3+
A tool to download maps from Dynmap and JourneyMap.

build.bat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
call .\venv\Scripts\activate
2+
start pyinstaller main.spec

controllers/about.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import webbrowser
2+
3+
from controllers.controller import Controller
4+
5+
6+
class AboutController(Controller):
7+
base_url = 'https://github.com/ryanbester/minecraft-map-tools'
8+
9+
@staticmethod
10+
def open_github_link():
11+
webbrowser.open_new(AboutController.base_url)
12+
13+
@staticmethod
14+
def open_issues_link():
15+
webbrowser.open_new('{}/issues'.format(AboutController.base_url))

controllers/configprovider.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import json
2+
import os
3+
4+
from models.config import Config
5+
from models.server import Server
6+
7+
8+
class ConfigProvider:
9+
def __init__(self, config: Config, config_dir: str, filename: str):
10+
self.config = config
11+
self.config_dir = config_dir
12+
self.filename = filename
13+
14+
def ensure_dir_exists(self):
15+
os.makedirs(self.config_dir, exist_ok=True)
16+
17+
def read_config(self):
18+
self.ensure_dir_exists()
19+
20+
try:
21+
with open(os.path.join(self.config_dir, self.filename), 'r') as f:
22+
config = json.load(f)
23+
self.config.servers = []
24+
for server in config['servers']:
25+
self.config.servers.append(Server(
26+
server['name'], server['dynmap_url'], server['journeymap_url'], server['ignore_ssl_cert']
27+
))
28+
except (OSError, json.JSONDecodeError, KeyError):
29+
self.config.servers = []
30+
31+
def save_config(self):
32+
self.ensure_dir_exists()
33+
34+
with open(os.path.join(self.config_dir, self.filename), 'w') as f:
35+
json.dump(self.config, f, default=vars)

controllers/controller.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from models.config import Config
2+
from core.state import State
3+
from views.view import View
4+
5+
6+
class Controller:
7+
def __init__(self, base_dir: str, config: Config, state: State, view: View):
8+
self.base_dir = base_dir
9+
self.config = config
10+
self.state = state
11+
self.view = view
12+
13+
def build_view(self, parent):
14+
self.view.build_view(parent, self)

controllers/download.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import os.path
2+
from threading import Thread
3+
from typing import Optional
4+
5+
from controllers.controller import Controller
6+
from core.dynmap import Dynmap
7+
from core.dynmapcoords import DynmapCoords
8+
from core.map import Map
9+
from core.state import State
10+
from models.config import Config
11+
from views.view import View
12+
13+
14+
class DownloadMcCoordsController(Controller):
15+
pass
16+
17+
18+
class DownloadGridRefController(Controller):
19+
pass
20+
21+
22+
class DownloadDynmapTileController(Controller):
23+
pass
24+
25+
26+
class DownloadController(Controller):
27+
progress: int
28+
progress_msg: str
29+
download_thread: Optional[Thread]
30+
31+
def __init__(self, base_dir: str, config: Config, state: State, view: View,
32+
mc_coords_controller: DownloadMcCoordsController,
33+
grid_ref_controller: DownloadGridRefController,
34+
dynmap_tile_controller: DownloadDynmapTileController):
35+
super().__init__(base_dir, config, state, view)
36+
self.mc_coords_controller = mc_coords_controller
37+
self.grid_ref_controller = grid_ref_controller
38+
self.dynmap_tile_controller = dynmap_tile_controller
39+
40+
self.download_thread = None
41+
42+
def server_change(self):
43+
self.view.server_change()
44+
45+
def start_download(self):
46+
self.progress_msg = ''
47+
self.progress = 0
48+
self.download_thread = Thread(target=self.download)
49+
self.download_thread.start()
50+
self.view.poll_progress()
51+
52+
# noinspection PyBroadException
53+
def download(self):
54+
try:
55+
user_input = self.view.get_input()
56+
except ValueError as e:
57+
self.view.display_error(str(e))
58+
return
59+
60+
tiles_dir = os.path.join(user_input['output_dir'], 'tiles')
61+
62+
try:
63+
if not os.path.isdir(tiles_dir):
64+
os.makedirs(tiles_dir)
65+
except OSError:
66+
self.view.display_error('Failed to create tiles directory')
67+
return
68+
69+
input_range = user_input['range']
70+
71+
if user_input['source'] == 'Dynmap':
72+
dynmap_config = Dynmap.get_config(self.state.selected_server)
73+
map_obj = Dynmap.get_map_obj(dynmap_config, user_input['map_id'])
74+
75+
if user_input['coord_mode'] == 'mc':
76+
from_tile_x, from_tile_y = DynmapCoords.mc_to_tile(map_obj, input_range['zoom'],
77+
input_range['from_x'], input_range['from_y'],
78+
input_range['from_z']
79+
)
80+
to_tile_x, to_tile_y = DynmapCoords.mc_to_tile(map_obj, input_range['zoom'],
81+
input_range['to_x'], input_range['to_y'],
82+
input_range['to_z']
83+
)
84+
85+
map_id = user_input['map_id'].split(' - ')
86+
world_name, map_name = map_id[0], map_id[1]
87+
88+
try:
89+
stitch_args = Dynmap.download_tiles(tiles_dir, self.state.selected_server.dynmap_url, world_name,
90+
map_name,
91+
input_range['zoom'], from_tile_x, from_tile_y,
92+
to_tile_x, to_tile_y, self.update_progress)
93+
except Exception:
94+
self.view.display_error('Failed to download tiles')
95+
return
96+
97+
try:
98+
Map.stitch(tiles_dir, user_input['output_dir'], stitch_args, self.update_progress)
99+
except Exception:
100+
self.view.display_error('Failed to stitch tiles')
101+
return
102+
103+
self.update_progress('Idle', 0, 1)
104+
105+
def update_progress(self, message: str, i, total):
106+
self.progress_msg = message
107+
self.progress = int((i / total) * 100)

controllers/main.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from controllers.about import AboutController
2+
from controllers.controller import Controller
3+
from controllers.download import DownloadController
4+
from controllers.servers import ServersController
5+
from controllers.utilities import UtilitiesController
6+
from core.state import State
7+
from models.config import Config
8+
from views.view import View
9+
10+
11+
class MainController(Controller):
12+
def __init__(self, base_dir: str, config: Config, state: State, view: View, servers_controller: ServersController,
13+
download_controller: DownloadController, utilities_controller: UtilitiesController,
14+
about_controller: AboutController):
15+
super().__init__(base_dir, config, state, view)
16+
17+
self.servers_controller = servers_controller
18+
self.download_controller = download_controller
19+
self.utilities_controller = utilities_controller
20+
self.about_controller = about_controller

controllers/servers.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import tkinter.messagebox
2+
from typing import Callable
3+
4+
import validators
5+
6+
from controllers.configprovider import ConfigProvider
7+
from controllers.controller import Controller
8+
from core.state import State
9+
from models.config import Config
10+
from models.server import Server
11+
from views.view import View
12+
13+
14+
class EditServerController(Controller):
15+
update_servers_dropdown: Callable
16+
title: str
17+
server: Server
18+
19+
def __init__(self, base_dir: str, config: Config, state: State, view: View, config_provider: ConfigProvider):
20+
super().__init__(base_dir, config, state, view)
21+
self.config_provider = config_provider
22+
23+
def build_view(self, parent, update_servers_dropdown: Callable = None, title='Edit Server', server: Server = None):
24+
self.update_servers_dropdown = update_servers_dropdown
25+
self.title = title
26+
self.server = server
27+
super().build_view(parent)
28+
29+
def save(self):
30+
try:
31+
input_server: Server = self.view.get_input()
32+
except ValueError as e:
33+
self.view.display_error(str(e))
34+
return
35+
36+
if len(input_server.name) < 1:
37+
self.view.display_error('Server name cannot be blank')
38+
return
39+
40+
if len(input_server.dynmap_url) > 0 and validators.url(input_server.dynmap_url) is not True:
41+
self.view.display_error('Dynmap URL is not valid')
42+
return
43+
44+
if len(input_server.journeymap_url) > 0 and validators.url(input_server.journeymap_url) is not True:
45+
self.view.display_error('JourneyMap URL is not valid')
46+
return
47+
48+
if self.server is not None:
49+
# Edit existing
50+
to_update = None
51+
for config_server in self.config.servers:
52+
if config_server.name == self.server.name:
53+
to_update = config_server
54+
55+
if to_update is not None:
56+
to_update.name = input_server.name
57+
to_update.dynmap_url = input_server.dynmap_url
58+
to_update.journeymap_url = input_server.journeymap_url
59+
to_update.ignore_ssl_cert = input_server.ignore_ssl_cert
60+
else:
61+
# Add new
62+
self.config.servers.append(input_server)
63+
64+
try:
65+
self.config_provider.save_config()
66+
except (OSError, ValueError):
67+
self.view.display_error('Failed to save server details')
68+
return
69+
70+
if self.update_servers_dropdown is not None:
71+
self.update_servers_dropdown()
72+
73+
self.state.call_server_change_callbacks()
74+
75+
self.view.edit_server_win.destroy()
76+
77+
78+
class ServersController(Controller):
79+
def __init__(self, base_dir: str, config: Config, state: State, view: View, config_provider: ConfigProvider,
80+
edit_server_controller: EditServerController):
81+
super().__init__(base_dir, config, state, view)
82+
self.config_provider = config_provider
83+
self.edit_server_controller = edit_server_controller
84+
85+
def select_server(self, server_name):
86+
for config_server in self.config.servers:
87+
if config_server.name == server_name:
88+
self.state.selected_server = config_server
89+
self.state.call_server_change_callbacks()
90+
91+
def add_server(self):
92+
self.edit_server_controller.build_view(self.state.main_win, self.view.update_servers_dropdown, 'Add server')
93+
94+
def edit_server(self):
95+
self.edit_server_controller.build_view(self.state.main_win, self.view.update_servers_dropdown,
96+
server=self.state.selected_server)
97+
98+
def delete_server(self):
99+
for config_server in self.config.servers:
100+
if config_server.name == self.state.selected_server.name:
101+
self.config.servers.remove(config_server)
102+
103+
try:
104+
self.config_provider.save_config()
105+
except (OSError, ValueError):
106+
tkinter.messagebox.showerror(self.state.main_win.title(), 'Failed to save servers')
107+
return
108+
109+
self.view.update_servers_dropdown()
110+
self.state.call_server_change_callbacks()

0 commit comments

Comments
 (0)