1 /*
2 * Copyright (c) 2017-2019 sel-project
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 */
23 /**
24 * Copyright: Copyright (c) 2017-2019 sel-project
25 * License: MIT
26 * Authors: Kripth
27 * Source: $(HTTP github.com/sel-project/selery/source/selery/util/resourcepack.d, selery/util/resourcepack.d)
28 */
29 module selery.util.resourcepack;
30
31 import std.algorithm : canFind;
32 import std.concurrency : Tid, send;
33 import std.conv : to;
34 import std.file : read, dirEntries, SpanMode, isFile;
35 import std.json : JSONValue;
36 import std.random : uniform;
37 import std.regex : ctRegex, replaceAll;
38 import std.socket;
39 import std..string : replace, join, indexOf, startsWith;
40 import std.uuid : UUID;
41 import std.zip;
42
43 import sel.net.http : StatusCodes, Response;
44
45 import selery.about : Software;
46 import selery.node.server : NodeServer;
47 import selery.util.tuple : Tuple;
48
49 auto createResourcePacks(shared NodeServer server, UUID uuid, string[] textures) {
50
51 auto java2 = new ZipArchive();
52 auto java3 = new ZipArchive();
53 auto pocket = new ZipArchive();
54
55 void add(ArchiveMember member) {
56 java2.addMember(member);
57 java3.addMember(member);
58 }
59
60 // add sel's modified textures to minecraft and pocket resource packs
61 foreach(t ; textures) {
62 foreach(string file ; dirEntries(t, SpanMode.breadth)) {
63 if(isFile(file)) {
64 immutable name = replace(file[t.length..$], "\\", "/");
65 auto data = cast(ubyte[])read(file);
66 add(create("assets/minecraft/textures/" ~ name, data));
67 pocket.addMember(create("textures/" ~ name, data));
68 }
69 }
70 }
71
72 // add icon
73 auto icon = cast(ubyte[])server.config.files.readAsset("icon.png");
74 add(create("pack.png", icon));
75 pocket.addMember(create("pack_icon.png", icon));
76
77 // create minecraft's manifest
78 auto description = JSONValue("The default look of Selery");
79 java2.addMember(create("pack.mcmeta", cast(ubyte[])JSONValue(["pack": JSONValue(["pack_format": JSONValue(2), "description": description])]).toString()));
80 java3.addMember(create("pack.mcmeta", cast(ubyte[])JSONValue(["pack": JSONValue(["pack_format": JSONValue(3), "description": description])]).toString()));
81
82 // create pocket's manifest
83 auto vers = JSONValue(cast(ubyte[])Software.versions);
84 pocket.addMember(create("manifest.json", cast(ubyte[])JSONValue(["format_version": JSONValue(1), "header": JSONValue(["description": description, "name": JSONValue("SEL"), "uuid": JSONValue(uuid.toString()), "version": vers]), "modules": JSONValue([JSONValue(["description": description, "type": JSONValue("resources"), "uuid": JSONValue(server.nextUUID.toString()), "version": vers])])]).toString()));
85
86 return Tuple!(void[], "java2", void[], "java3", void[], "pocket1")(java2.build(), java3.build(), pocket.build());
87
88 }
89
90 private ArchiveMember create(string name, ubyte[] data) {
91 auto ret = new ArchiveMember();
92 ret.name = name;
93 ret.expandedData(data);
94 ret.compressionMethod = CompressionMethod.deflate;
95 return ret;
96 }
97
98 void serveResourcePacks(Tid server, string pack2, string pack3) {
99
100 auto port = uniform(ushort(999), ushort.max);
101
102 auto socket = new TcpSocket();
103 socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
104 socket.bind(new InternetAddress("0.0.0.0", port));
105 socket.listen(16);
106 socket.blocking = true;
107
108 send(server, port);
109
110 char[] buffer = new char[6];
111
112 const(void)[] response2 = Response(StatusCodes.ok, ["Content-Type": "application/zip", "Server": Software.display], pack2).toString();
113 const(void)[] response3 = Response(StatusCodes.ok, ["Content-Type": "application/zip", "Server": Software.display], pack3).toString();
114
115 while(true) {
116
117 //TODO create a non-blocking handler
118
119 auto client = socket.accept();
120
121 auto r = client.receive(buffer);
122 if(r == 6) { // length of the buffer
123 if(buffer[0..5] == "GET /") {
124 if(buffer[5] == '2') client.send(response2);
125 else if(buffer[5] == '3') client.send(response3);
126 else client.close();
127 }
128 }
129
130 }
131
132 }