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 }