Browse Source

re-add bones logging and messages

resolves #59 (this commit also adds a modding page to the webapp)
master
poikilos 6 years ago
committed by Jacob Gustafson
parent
commit
4c67ad1882
  1. 1
      etc/install-ENLIVEN-minetest_game.sh
  2. 12
      patches/Bucket_Game-basis/mods/LICENSE
  3. 1
      patches/Bucket_Game-basis/mods/codercore/00README
  4. 12
      patches/Bucket_Game-basis/mods/codercore/bones/README.txt
  5. 249
      patches/Bucket_Game-basis/mods/codercore/bones/init.lua
  6. 58
      patches/Bucket_Game-basis/mods/codercore/bones/license.txt
  7. 0
      patches/Bucket_Game-basis/mods/codercore/modpack.txt
  8. 20
      patches/Bucket_Game-basis/mods/codercore/oldcoder.txt
  9. 10
      patches/Bucket_Game-patched/LICENSE-Poikilos.md
  10. 275
      patches/Bucket_Game-patched/mods/codercore/bones/init.lua
  11. 3
      webapp/install-mts.sh
  12. 5
      webapp/server.js
  13. 80
      webapp/views/pages/modding.ejs
  14. 2
      webapp/views/pages/skin-selection-form.ejs
  15. 1
      webapp/views/partials/header.ejs

1
etc/install-ENLIVEN-minetest_game.sh

@ -657,6 +657,7 @@ echo "Installing AntumMT's modernized fork of Kilarin's compassgps (NOT TeTpaAka
echo "Installing AntumMT's modernized fork of Kilarin's compassgps (NOT TeTpaAka, nor Echo, nor PilzAdam compass) <https://forum.minetest.net/viewtopic.php?t=9373>" > ~/Downloads/compassgps.txt
# https://github.com/Kilarin/compassgps.git
#add_git_mod compassgps mod-compassgps https://github.com/AntumMT/mod-compassgps.git
# TODO: rebase from Kilarin's version (merged AntumMT's fix and my readme clarifications) & use good texture for inventory
add_git_mod compassgps compassgps https://github.com/poikilos/compassgps.git
remove_mod helicopter

12
patches/Bucket_Game-basis/mods/LICENSE

@ -0,0 +1,12 @@
For license information, see the following files, where they exist, in
each modpack or mod:
oldcoder.txt
LICENSE
LICENSE.txt
license.txt
README.md
README.txt
readme.txt
and/or files with similar names.

1
patches/Bucket_Game-basis/mods/codercore/00README

@ -0,0 +1 @@
See "oldcoder.txt".

12
patches/Bucket_Game-basis/mods/codercore/bones/README.txt

@ -0,0 +1,12 @@
Minetest Game mod: bones
========================
See license.txt for license information.
Authors of source code
----------------------
Originally by PilzAdam (MIT)
Various Minetest developers and contributors (MIT)
Authors of media (textures)
---------------------------
All textures: paramat (CC BY-SA 3.0)

249
patches/Bucket_Game-basis/mods/codercore/bones/init.lua

@ -0,0 +1,249 @@
-- Minetest 0.4 mod: bones
-- See README.txt for licensing and other information.
local function is_owner(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
return true
end
return false
end
local bones_formspec =
"size[8,9]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[current_name;main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 1200
local share_bones_time_early = tonumber(minetest.setting_get("share_bones_time_early")) or share_bones_time / 4
minetest.register_node("bones:bones", {
description = "Bones",
tiles = {
"bones_top.png^[transform2",
"bones_bottom.png",
"bones_side.png",
"bones_side.png",
"bones_rear.png",
"bones_front.png"
},
paramtype2 = "facedir",
groups = {dig_immediate = 2},
sounds = default.node_sound_gravel_defaults(),
can_dig = function(pos, player)
local inv = minetest.get_meta(pos):get_inventory()
local name = ""
if player then
name = player:get_player_name()
end
return is_owner(pos, name) and inv:is_empty("main")
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if is_owner(pos, player:get_player_name()) then
return count
end
return 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if is_owner(pos, player:get_player_name()) then
return stack:get_count()
end
return 0
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if meta:get_inventory():is_empty("main") then
minetest.remove_node(pos)
end
end,
on_punch = function(pos, node, player)
if not is_owner(pos, player:get_player_name()) then
return
end
if minetest.get_meta(pos):get_string("infotext") == "" then
return
end
local inv = minetest.get_meta(pos):get_inventory()
local player_inv = player:get_inventory()
local has_space = true
for i = 1, inv:get_size("main") do
local stk = inv:get_stack("main", i)
if player_inv:room_for_item("main", stk) then
inv:set_stack("main", i, nil)
player_inv:add_item("main", stk)
else
has_space = false
break
end
end
-- remove bones if player emptied them
if has_space then
if player_inv:room_for_item("main", {name = "bones:bones"}) then
player_inv:add_item("main", {name = "bones:bones"})
else
minetest.add_item(pos,"bones:bones")
end
minetest.remove_node(pos)
end
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local time = meta:get_int("time") + elapsed
if time >= share_bones_time then
meta:set_string("infotext", meta:get_string("owner") .. "'s old bones")
meta:set_string("owner", "")
else
meta:set_int("time", time)
return true
end
end,
on_blast = function(pos)
end,
})
local function may_replace(pos, player)
local node_name = minetest.get_node(pos).name
local node_definition = minetest.registered_nodes[node_name]
-- if the node is unknown, we return false
if not node_definition then
return false
end
-- allow replacing air and liquids
if node_name == "air" or node_definition.liquidtype ~= "none" then
return true
end
-- don't replace filled chests and other nodes that don't allow it
local can_dig_func = node_definition.can_dig
if can_dig_func and not can_dig_func(pos, player) then
return false
end
-- default to each nodes buildable_to; if a placed block would replace it, why shouldn't bones?
-- flowers being squished by bones are more realistical than a squished stone, too
-- exception are of course any protected buildable_to
return node_definition.buildable_to and not minetest.is_protected(pos, player:get_player_name())
end
local drop = function(pos, itemstack)
local obj = minetest.add_item(pos, itemstack:take_item(itemstack:get_count()))
if obj then
obj:setvelocity({
x = math.random(-10, 10) / 9,
y = 5,
z = math.random(-10, 10) / 9,
})
end
end
minetest.register_on_dieplayer(function(player)
local bones_mode = minetest.setting_get("bones_mode") or "bones"
if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then
bones_mode = "bones"
end
-- return if keep inventory set or in creative mode
if bones_mode == "keep" or (creative and creative.is_enabled_for
and creative.is_enabled_for(player:get_player_name())) then
return
end
local player_inv = player:get_inventory()
if player_inv:is_empty("main") and
player_inv:is_empty("craft") then
return
end
local pos = vector.round(player:getpos())
local player_name = player:get_player_name()
-- check if it's possible to place bones, if not find space near player
if bones_mode == "bones" and not may_replace(pos, player) then
local air = minetest.find_node_near(pos, 1, {"air"})
if air and not minetest.is_protected(air, player_name) then
pos = air
else
bones_mode = "drop"
end
end
if bones_mode == "drop" then
-- drop inventory items
for i = 1, player_inv:get_size("main") do
drop(pos, player_inv:get_stack("main", i))
end
player_inv:set_list("main", {})
-- drop crafting grid items
for i = 1, player_inv:get_size("craft") do
drop(pos, player_inv:get_stack("craft", i))
end
player_inv:set_list("craft", {})
drop(pos, ItemStack("bones:bones"))
return
end
local param2 = minetest.dir_to_facedir(player:get_look_dir())
minetest.set_node(pos, {name = "bones:bones", param2 = param2})
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("main", 8 * 4)
inv:set_list("main", player_inv:get_list("main"))
for i = 1, player_inv:get_size("craft") do
local stack = player_inv:get_stack("craft", i)
if inv:room_for_item("main", stack) then
inv:add_item("main", stack)
else
--drop if no space left
drop(pos, stack)
end
end
player_inv:set_list("main", {})
player_inv:set_list("craft", {})
meta:set_string("formspec", bones_formspec)
meta:set_string("owner", player_name)
if share_bones_time ~= 0 then
meta:set_string("infotext", player_name .. "'s fresh bones")
if share_bones_time_early == 0 or not minetest.is_protected(pos, player_name) then
meta:set_int("time", 0)
else
meta:set_int("time", (share_bones_time - share_bones_time_early))
end
minetest.get_node_timer(pos):start(10)
else
meta:set_string("infotext", player_name.."'s bones")
end
end)

58
patches/Bucket_Game-basis/mods/codercore/bones/license.txt

@ -0,0 +1,58 @@
License of source code
----------------------
The MIT License (MIT)
Copyright (C) 2012-2016 PilzAdam
Copyright (C) 2012-2016 Various Minetest developers and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
For more details:
https://opensource.org/licenses/MIT
Licenses of media (textures)
----------------------------
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
Copyright (C) 2016 paramat
You are free to:
Share — copy and redistribute the material in any medium or format.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit, provide a link to the license, and
indicate if changes were made. You may do so in any reasonable manner, but not in any way
that suggests the licensor endorses you or your use.
ShareAlike — If you remix, transform, or build upon the material, you must distribute
your contributions under the same license as the original.
No additional restrictions — You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.
Notices:
You do not have to comply with the license for elements of the material in the public
domain or where your use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary
for your intended use. For example, other rights such as publicity, privacy, or moral
rights may limit how you use the material.

0
patches/Bucket_Game-basis/mods/codercore/modpack.txt

20
patches/Bucket_Game-basis/mods/codercore/oldcoder.txt

@ -0,0 +1,20 @@
Name: stairs
Source: Modified upstream mod (do not replace)
License: See "license.txt"
----------------------------------------------------------------------
1. This is a copy of the standard MT "stairs" mod from circa mid-2016.
It should not, ever, be synced with upstream or placing and/or screw-
driver may not work correctly.
As a related note, the following upstream commit in "minetest_game",
which applies to "stairs", is bad and shouldn't be used:
e6d0d775e3533b7b91b292e13c800b9737e057fd
----------------------------------------------------------------------
2. Changes include:
2a. Added the files "00README" and "oldcoder.txt" (this file).

10
patches/Bucket_Game-patched/LICENSE-Poikilos.md

@ -0,0 +1,10 @@
Mods in this folder are based on Bucket_Game, and modified by
[Poikilos](https://github.com/poikilos).
See ../Bucket_Game-basis/mods/LICENSE and additional text files in
subdirectories of Bucket_Game-basis/mods for other information.
The Bucket_Game-basis directory is kept to keep track of what version of
Bucket_Game was modified. The version of Bucket_Game used for each file
is not guaranteed, but the version used as a basis for
Bucket_Game-patched/mods files is guaranteed.

275
patches/Bucket_Game-patched/mods/codercore/bones/init.lua

@ -0,0 +1,275 @@
-- Minetest 0.4 mod: bones
-- See README.txt for licensing and other information.
local function is_owner(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
return true
end
return false
end
local bones_formspec =
"size[8,9]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[current_name;main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 1200
local share_bones_time_early = tonumber(minetest.setting_get("share_bones_time_early")) or share_bones_time / 4
minetest.register_node("bones:bones", {
description = "Bones",
tiles = {
"bones_top.png^[transform2",
"bones_bottom.png",
"bones_side.png",
"bones_side.png",
"bones_rear.png",
"bones_front.png"
},
paramtype2 = "facedir",
groups = {dig_immediate = 2},
sounds = default.node_sound_gravel_defaults(),
can_dig = function(pos, player)
local inv = minetest.get_meta(pos):get_inventory()
local name = ""
if player then
name = player:get_player_name()
end
return is_owner(pos, name) and inv:is_empty("main")
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if is_owner(pos, player:get_player_name()) then
return count
end
return 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if is_owner(pos, player:get_player_name()) then
return stack:get_count()
end
return 0
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if meta:get_inventory():is_empty("main") then
minetest.remove_node(pos)
end
end,
on_punch = function(pos, node, player)
if not is_owner(pos, player:get_player_name()) then
return
end
if minetest.get_meta(pos):get_string("infotext") == "" then
return
end
local inv = minetest.get_meta(pos):get_inventory()
local player_inv = player:get_inventory()
local has_space = true
for i = 1, inv:get_size("main") do
local stk = inv:get_stack("main", i)
if player_inv:room_for_item("main", stk) then
inv:set_stack("main", i, nil)
player_inv:add_item("main", stk)
else
has_space = false
break
end
end
-- remove bones if player emptied them
if has_space then
if player_inv:room_for_item("main", {name = "bones:bones"}) then
player_inv:add_item("main", {name = "bones:bones"})
else
minetest.add_item(pos,"bones:bones")
end
minetest.remove_node(pos)
end
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local time = meta:get_int("time") + elapsed
if time >= share_bones_time then
meta:set_string("infotext", meta:get_string("owner") .. "'s old bones")
meta:set_string("owner", "")
else
meta:set_int("time", time)
return true
end
end,
on_blast = function(pos)
end,
})
local function may_replace(pos, player)
local node_name = minetest.get_node(pos).name
local node_definition = minetest.registered_nodes[node_name]
-- if the node is unknown, we return false
if not node_definition then
return false
end
-- allow replacing air and liquids
if node_name == "air" or node_definition.liquidtype ~= "none" then
return true
end
-- don't replace filled chests and other nodes that don't allow it
local can_dig_func = node_definition.can_dig
if can_dig_func and not can_dig_func(pos, player) then
return false
end
-- default to each nodes buildable_to; if a placed block would replace it, why shouldn't bones?
-- flowers being squished by bones are more realistical than a squished stone, too
-- exception are of course any protected buildable_to
return node_definition.buildable_to and not minetest.is_protected(pos, player:get_player_name())
end
local drop = function(pos, itemstack)
local obj = minetest.add_item(pos, itemstack:take_item(itemstack:get_count()))
if obj then
obj:setvelocity({
x = math.random(-10, 10) / 9,
y = 5,
z = math.random(-10, 10) / 9,
})
end
end
minetest.register_on_dieplayer(function(player)
local bones_mode = minetest.setting_get("bones_mode") or "bones"
if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then
bones_mode = "bones"
end
local bones_position_message = minetest.settings:get_bool("bones_position_message") == true
local pos = vector.round(player:getpos())
local player_name = player:get_player_name()
local pos_string = minetest.pos_to_string(pos)
-- return if keep inventory set or in creative mode
if bones_mode == "keep" or (creative and creative.is_enabled_for
and creative.is_enabled_for(player:get_player_name())) then
minetest.log("action", player_name .. " dies at " .. pos_string ..
". No bones remain")
if bones_position_message then
minetest.chat_send_player(player_name, player_name .. " died at " .. pos_string .. ".")
end
return
end
local player_inv = player:get_inventory()
if player_inv:is_empty("main") and
player_inv:is_empty("craft") then
minetest.log("action", player_name .. " dies at " .. pos_string ..
". No bones remain")
if bones_position_message then
minetest.chat_send_player(player_name, player_name .. " died at " .. pos_string .. ".")
end
return
end
-- check if it's possible to place bones, if not find space near player
if bones_mode == "bones" and not may_replace(pos, player) then
local air = minetest.find_node_near(pos, 1, {"air"})
if air and not minetest.is_protected(air, player_name) then
pos = air
else
bones_mode = "drop"
end
end
if bones_mode == "drop" then
-- drop inventory items
for i = 1, player_inv:get_size("main") do
drop(pos, player_inv:get_stack("main", i))
end
player_inv:set_list("main", {})
-- drop crafting grid items
for i = 1, player_inv:get_size("craft") do
drop(pos, player_inv:get_stack("craft", i))
end
player_inv:set_list("craft", {})
drop(pos, ItemStack("bones:bones"))
minetest.log("action", player_name .. " dies at " .. pos_string ..
". Inventory dropped")
if bones_position_message then
minetest.chat_send_player(player_name, player_name .. " died at " .. pos_string ..
", and dropped their inventory.")
end
return
end
local param2 = minetest.dir_to_facedir(player:get_look_dir())
minetest.set_node(pos, {name = "bones:bones", param2 = param2})
minetest.log("action", player_name .. " dies at " .. pos_string ..
". Bones remain")
if bones_position_message then
minetest.chat_send_player(player_name, player_name .. " died at " .. pos_string ..
", and bones remain.")
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("main", 8 * 4)
inv:set_list("main", player_inv:get_list("main"))
for i = 1, player_inv:get_size("craft") do
local stack = player_inv:get_stack("craft", i)
if inv:room_for_item("main", stack) then
inv:add_item("main", stack)
else
--drop if no space left
drop(pos, stack)
end
end
player_inv:set_list("main", {})
player_inv:set_list("craft", {})
meta:set_string("formspec", bones_formspec)
meta:set_string("owner", player_name)
if share_bones_time ~= 0 then
meta:set_string("infotext", player_name .. "'s fresh bones")
if share_bones_time_early == 0 or not minetest.is_protected(pos, player_name) then
meta:set_int("time", 0)
else
meta:set_int("time", (share_bones_time - share_bones_time_early))
end
minetest.get_node_timer(pos):start(10)
else
meta:set_string("infotext", player_name.."'s bones")
end
end)

3
webapp/install-mts.sh

@ -5,6 +5,7 @@ if [ ! -d "$flag_dir" ]; then
echo "ERROR: missing $flag_dir"
exit 1
fi
enliven_path=".."
cd "$extracted_name"
extra_options=""
if [ "@$1" = "@--client" ]; then
@ -104,3 +105,5 @@ else
rsync -rt "$flag_dir/mods/$mod_name" "$HOME/minetest/games/ENLIVEN/mods"
# cp -f "$flag_dir/mods/LICENSE" "$HOME/minetest/games/ENLIVEN/mods/LICENSE"
fi
rsync -rt "$enliven_path/patches/subgame/menu/" "$HOME/minetest/games/ENLIVEN/menu"
rsync -rt "$enliven_path/patches/Bucket_Game-patched/" "$HOME/minetest/games/ENLIVEN"

5
webapp/server.js

@ -365,6 +365,11 @@ function readLog() {
}
}
app.get('/modding', function(req, res, next) {
res.render('pages/modding', {
msg: "",
});
});
app.get('/skin-upload-form', function(req, res, next) {
//var ending = "";
//ending += '<a href="/">Back to Main Site</a><br/>' + "\n";

80
webapp/views/pages/modding.ejs

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="en">
<head>
<% include ../partials/head %>
</head>
<body class="container">
<header>
<% include ../partials/header %>
</header>
<main>
<div class="row">
<div class="col-sm-8">
<div class="jumbotron">
<h1>EnlivenMinetest</h1>
<p>Modding.</p>
<h2>&nbsp;<!--result--></h2>
<p><%= msg %></p>
</div>
<div>
<h2>Resources</h2>
<ul>
<li><a href="https://git.minetest.org/minetest/minetest/src/branch/master/doc/lua_api.txt">doc/lua_api.txt</a> on minetest.org (drastically improved over the original github version)</li>
</ul>
<h3>Historical Resources</h3>
(may only apply to minetest.net (same as GitHub.com/minetest) releases which have a heavily changed API without backward compatibility)
<ul>
<li><a href="https://rubenwardy.com/minetest_modding_book/en/index.html">The Minetest Modding Book</a> on rubenwardy.com</li>
</ul>
<h2>Tips</h2>
* In singleplayer, you have the `privs` privilege. You can give yourself
all privileges by typing: "/grant singleplayer all" (The same can be
done with your own username if you are hosting a world).
* You can test small pieces of code using the "Run Lua" and "Formspec
Tester" features of the CoderEdit or WorldEdit GUI if you have the
privilege.
<h2>Common Items for Moderators to Spawn</h2>
<pre>
glass default:obsidian_glass
glass moreblocks:wood_tile
glass technic:concrete
stairs:stair_wood stairs:stair_desert_stonebrick
stairs:stair_wood stairs:stair_desert_stonebrick
stairs:stair_wood stairs:stair_goldblock
stairs:stair_lapis_brick
mossycobble lapis:lapis_brick
mesecons_lightstone:lightstone_red_off
mesecons_torch:mesecon_torch_on
wool:white
technic:stainless_steel_block
lapis:base_lazurite_block
#white seams:
lapis:column_lazurite_brick
lapis:base_lazurite_brick
#subtle seams:
lapis:column_lapis_brick
lapis:base_lapis_brick
lapis:base_lapis_block
lapis:column_lapis_block
default:fence_aspen_wood
</pre>
</div>
</div>
</div>
</main>
<footer>
<% include ../partials/footer %>
</footer>
</body>
</html>

2
webapp/views/pages/skin-selection-form.ejs

@ -56,7 +56,7 @@
<div class="col-sm-4">
<div class="well">
<h3>Links:</h3>
<p><a href="https://poikilos.dyndns.org">Server Page</a></p>
<p><a href="https://poikilos.dyndns.org">Center of the Sun</a></p>
</div>
</div>

1
webapp/views/partials/header.ejs

@ -11,6 +11,7 @@
<li><a href="/">Home</a></li>
<li><a href="/skin-upload-form">Upload Skin</a></li>
<li><a href="/skin-selection-form">Choose Skin</a></li>
<li><a href="/modding">Modding</a></li>
</ul>
</div>

Loading…
Cancel
Save