mapmerge update + mats properly work
This commit is contained in:
@@ -81,8 +81,8 @@ Simple datum which is instanced once per type and is used for every object of sa
|
||||
if(material_flags & MATERIAL_ADD_PREFIX)
|
||||
source.name = "[name] [source.name]"
|
||||
|
||||
if(beauty_modifier)
|
||||
addtimer(CALLBACK(source, /datum.proc/_AddElement, list(/datum/element/beauty, beauty_modifier * amount)), 0)
|
||||
// if(beauty_modifier) returnign in hardsync2 if i ever port ebeauty cmp
|
||||
// addtimer(CALLBACK(source, /datum.proc/_AddElement, list(/datum/element/beauty, beauty_modifier * amount)), 0)
|
||||
|
||||
if(istype(source, /obj)) //objs
|
||||
on_applied_obj(source, amount, material_flags)
|
||||
@@ -151,8 +151,8 @@ Simple datum which is instanced once per type and is used for every object of sa
|
||||
if(material_flags & MATERIAL_ADD_PREFIX)
|
||||
source.name = initial(source.name)
|
||||
|
||||
if(beauty_modifier) //component/beauty/InheritComponent() will handle the removal.
|
||||
addtimer(CALLBACK(source, /datum.proc/_AddElement, list(/datum/element/beauty, -beauty_modifier * amount)), 0)
|
||||
// if(beauty_modifier) //component/beauty/InheritComponent() will handle the removal.
|
||||
// addtimer(CALLBACK(source, /datum.proc/_AddElement, list(/datum/element/beauty, -beauty_modifier * amount)), 0)
|
||||
|
||||
if(istype(source, /obj)) //objs
|
||||
on_removed_obj(source, amount, material_flags)
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
overflow: hidden;
|
||||
background-color: #272727;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 16px;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.banbox {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
display: table;
|
||||
flex-direction: column;
|
||||
border: 1px solid #161616;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color:rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reason {
|
||||
display: table-cell;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.edit {
|
||||
display: table-cell;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.banned {
|
||||
background-color:#ff5555;
|
||||
}
|
||||
|
||||
.unbanned {
|
||||
background-color:#00b75c;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ body {
|
||||
font-size: 9pt;
|
||||
}
|
||||
.value {
|
||||
font-family: "consolas", monospace; /* consolas is better!! (was "Courier New") */
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 8pt;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -12,24 +12,26 @@ table.matrix {
|
||||
border-collapse: collapse; border-spacing: 0;
|
||||
font-size: 7pt;
|
||||
}
|
||||
table.matrix td{
|
||||
.matrix td{
|
||||
text-align: center;
|
||||
padding: 0 1ex 0ex 1ex;
|
||||
}
|
||||
table.matrixbrak {
|
||||
border-collapse: collapse; border-spacing: 0;
|
||||
}
|
||||
table.matrixbrak td.lbrak, table.matrixbrak td.rbrak{
|
||||
table.matrixbrak td.lbrak {
|
||||
width: 0.8ex;
|
||||
font-size: 50%;
|
||||
border-top: solid 0.25ex black;
|
||||
border-bottom: solid 0.25ex black;
|
||||
}
|
||||
table.matrixbrak td.lbrak {
|
||||
border-left: solid 0.5ex black;
|
||||
border-right: none;
|
||||
}
|
||||
table.matrixbrak td.rbrak {
|
||||
border-right: solid 0.5ex black;
|
||||
border-left: none;
|
||||
width: 0.8ex;
|
||||
font-size: 50%;
|
||||
border-top: solid 0.25ex black;
|
||||
border-bottom: solid 0.25ex black;
|
||||
border-right: solid 0.5ex black;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
+14
-1
@@ -76,7 +76,6 @@ a.icon img, .linkOn.icon img
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
ul
|
||||
{
|
||||
padding: 4px 0 0 10px;
|
||||
@@ -396,3 +395,17 @@ ul.sparse {
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.severity {
|
||||
margin:0px;
|
||||
padding: 1px 8px 1px 8px;
|
||||
border-radius: 25px;
|
||||
border: 1px solid #161616;
|
||||
background: #40628a;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.severity img {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -10,13 +10,15 @@
|
||||
color: #ef2f3c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bluetext {
|
||||
color: #517fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.neutraltext {
|
||||
font-weight: bold; /* If you feel these should have some color feel free to change */
|
||||
}
|
||||
}
|
||||
|
||||
.marooned {
|
||||
color: rgb(109, 109, 255); font-weight: bold;
|
||||
@@ -93,4 +95,4 @@ body {
|
||||
|
||||
.tooltip_container:hover .tooltip_hover {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,20 @@
|
||||
margin: 2px 2px 0 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dnaBlock
|
||||
{
|
||||
font-family: Fixed, monospace;
|
||||
float: left;
|
||||
}
|
||||
|
||||
a.incompleteBlock
|
||||
{
|
||||
background: #8a4040;
|
||||
}
|
||||
a.incompleteBlock:hover
|
||||
{
|
||||
color: #40628a;
|
||||
background: #ffffff;
|
||||
}
|
||||
img.selected
|
||||
{
|
||||
border: 1px solid blue;
|
||||
@@ -36,4 +43,4 @@ a.clean
|
||||
background: none;
|
||||
border: none;
|
||||
marging: none;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<div class='display'>
|
||||
<section>
|
||||
<span class='label'>State:</span>
|
||||
<div class='content' id="full_pressure">@{full_pressure}</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class='label'>Pressure:</span>
|
||||
<div class='content'>
|
||||
<div class='progressBar' id='per'>
|
||||
<div class='progressFill' style="width: @{per}"></div>
|
||||
<div class='progressLabel'>@{per}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class='label'>Handle:</span>
|
||||
<div class='content' id="flush">@{flush}</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class='label'>Eject:</span>
|
||||
<div class='content' id="contents">@{contents}</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class='label'>Compressor:</span>
|
||||
<div class='content' id="pressure_charging">@{pressure_charging}</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -1,103 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>OracleUI IDE</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
h1 {
|
||||
margin: 5px;
|
||||
}
|
||||
#template_src_container {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 0px;
|
||||
width: 50%;
|
||||
height: 75%;
|
||||
}
|
||||
#template_src {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
#data_src_container {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 75%;
|
||||
width: 50%;
|
||||
height: calc(25% - 20px);
|
||||
}
|
||||
#data_src {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
#output_container {
|
||||
position: absolute;
|
||||
left: calc(50% + 20px);
|
||||
top: 0%;
|
||||
width: calc(50% - 50px);
|
||||
height: calc(100% - 20px);
|
||||
}
|
||||
#output_frame {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 10px;
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="template_src_container">
|
||||
<h1>Content Template:</h1>
|
||||
<textarea id="template_src" onchange="updateBody();" onkeyup="updateBody();" onpaste="updateBody();">
|
||||
<div class='display'>
|
||||
<section>
|
||||
<span class='label'>Key:</span>
|
||||
<div class='content' id="key">@{key}</div>
|
||||
</section>
|
||||
</div>
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="data_src_container">
|
||||
<h1>Data:</h1>
|
||||
<textarea id="data_src" onchange="updateData();" onkeyup="updateData();" onpaste="updateData();">{"key":"value"}</textarea>
|
||||
</div>
|
||||
<div id="output_container">
|
||||
<h1>Output:</h1>
|
||||
<iframe name="output_frame" id="output_frame" onload="updateBody();"></iframe>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
function updateBody() {
|
||||
var body = document.getElementById('template_src').value
|
||||
var fields = JSON.parse(document.getElementById('data_src').value);
|
||||
for (var key in fields) {
|
||||
let value = fields[key];
|
||||
body = body.replace("@{" + key + "}", value)
|
||||
}
|
||||
var targetFrame = document.getElementById('output_frame')
|
||||
targetFrame.contentWindow.replaceContent(body);
|
||||
updateData();
|
||||
}
|
||||
function updateData() {
|
||||
var targetFrame = document.getElementById('output_frame');
|
||||
var json = document.getElementById('data_src').value;
|
||||
if(JSON.parse(json)) {
|
||||
targetFrame.contentWindow.updateFields(json);
|
||||
}
|
||||
}
|
||||
var url = document.URL,
|
||||
shortUrl=url.substring(0,url.lastIndexOf("/"));
|
||||
window.open(shortUrl + "/themes/nano/index.html", "output_frame");
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<head>
|
||||
<title>@{title}</title>
|
||||
<script src="sui-nano-jquery.min.js"></script>
|
||||
<script src="sui-nano-common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="sui-nano-common.css">
|
||||
</head>
|
||||
<body scroll=auto>
|
||||
<div class='uiWrapper'>
|
||||
<div class='uiTitleWrapper'><div class='uiTitle'><tt>@{title}</tt></div></div>
|
||||
<div class='uiContent' id='maincontent'>
|
||||
@{body}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,353 +0,0 @@
|
||||
body
|
||||
{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #272727;
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
line-height: 170%;
|
||||
cursor: default;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
background-color: #40628a;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
a, a:link, a:visited, a:active, .linkOn, .linkOff
|
||||
{
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
background: #40628a;
|
||||
border: 1px solid #161616;
|
||||
padding: 1px 4px 1px 4px;
|
||||
margin: 0 2px 0 0;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: #40628a;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
a.white, a.white:link, a.white:visited, a.white:active
|
||||
{
|
||||
color: #40628a;
|
||||
text-decoration: none;
|
||||
background: #ffffff;
|
||||
border: 1px solid #161616;
|
||||
padding: 1px 4px 1px 4px;
|
||||
margin: 0 2px 0 0;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
a.white:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #40628a;
|
||||
}
|
||||
|
||||
.active, a.active:link, a.active:visited, a.active:active, a.active:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #2f943c;
|
||||
border-color: #24722e;
|
||||
}
|
||||
|
||||
.disabled, a.disabled:link, a.disabled:visited, a.disabled:active, a.disabled:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #999999;
|
||||
border-color: #666666;
|
||||
}
|
||||
|
||||
a.icon, .linkOn.icon, .linkOff.icon
|
||||
{
|
||||
position: relative;
|
||||
padding: 1px 4px 2px 20px;
|
||||
}
|
||||
|
||||
a.icon img, .linkOn.icon img
|
||||
{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
ul
|
||||
{
|
||||
padding: 4px 0 0 10px;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li
|
||||
{
|
||||
padding: 0 0 2px 0;
|
||||
}
|
||||
|
||||
img, a img
|
||||
{
|
||||
border-style:none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
{
|
||||
margin: 0;
|
||||
padding: 16px 0 8px 0;
|
||||
color: #517087;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
h2
|
||||
{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.uiWrapper
|
||||
{
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.uiTitle
|
||||
{
|
||||
clear: both;
|
||||
padding: 6px 8px 6px 8px;
|
||||
border-bottom: 2px solid #161616;
|
||||
background: #383838;
|
||||
color: #98B0C3;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.uiTitle.icon
|
||||
{
|
||||
padding: 6px 8px 6px 42px;
|
||||
background-position: 2px 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.uiContent
|
||||
{
|
||||
clear: both;
|
||||
padding: 8px;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
}
|
||||
|
||||
.good
|
||||
{
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.average
|
||||
{
|
||||
color: #d09000;
|
||||
}
|
||||
|
||||
.bad
|
||||
{
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.highlight
|
||||
{
|
||||
color: #8BA5C4;
|
||||
}
|
||||
|
||||
.dark
|
||||
{
|
||||
color: #272727;
|
||||
}
|
||||
|
||||
.notice
|
||||
{
|
||||
position: relative;
|
||||
background: #E9C183;
|
||||
color: #15345A;
|
||||
font-size: 10px;
|
||||
font-style: italic;
|
||||
padding: 2px 4px 0 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.notice.icon
|
||||
{
|
||||
padding: 2px 4px 0 20px;
|
||||
}
|
||||
|
||||
.notice img
|
||||
{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
div.notice
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.statusDisplay
|
||||
{
|
||||
background: #000000;
|
||||
color: #ffffff;
|
||||
border: 1px solid #40628a;
|
||||
padding: 4px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.statusLabel
|
||||
{
|
||||
width: 138px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
color: #98B0C3;
|
||||
}
|
||||
|
||||
.statusValue
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.block
|
||||
{
|
||||
padding: 8px;
|
||||
margin: 10px 4px 4px 4px;
|
||||
border: 1px solid #40628a;
|
||||
background-color: #202020;
|
||||
}
|
||||
|
||||
.block h3
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.progressBar
|
||||
{
|
||||
position: relative;
|
||||
width: 185px;
|
||||
height: 14px;
|
||||
border: 1px solid #666666;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.progressLabel
|
||||
{
|
||||
top: -2px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.progressFill
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #40628a;
|
||||
overflow: hidden;
|
||||
transition: width 2.2s linear;
|
||||
}
|
||||
|
||||
.progressFill.good
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #00ff00;
|
||||
}
|
||||
|
||||
.progressFill.average
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #d09000;
|
||||
}
|
||||
|
||||
.progressFill.bad
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #ff0000;
|
||||
}
|
||||
|
||||
.progressFill.highlight
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #8BA5C4;
|
||||
}
|
||||
|
||||
.clearBoth
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.clearLeft
|
||||
{
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.clearRight
|
||||
{
|
||||
clear: right;
|
||||
}
|
||||
|
||||
.line
|
||||
{
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
section .label, section .content
|
||||
{
|
||||
display: table-cell;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
padding: 3px 2px
|
||||
}
|
||||
|
||||
section .label
|
||||
{
|
||||
width: 1%;
|
||||
padding-right: 32px;
|
||||
white-space: nowrap;
|
||||
color: #8ba5c4;
|
||||
}
|
||||
|
||||
section
|
||||
{
|
||||
display: table-row;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.display {
|
||||
width: calc(100% - 8px);
|
||||
padding: 4px;
|
||||
background-color: #000;
|
||||
background-color: rgba(0, 0, 0, .33);
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, .5);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)";
|
||||
filter: progid: DXImageTransform.Microsoft.gradient(startColorStr=#54000000, endColorStr=#54000000);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
function replaceContent(body) {
|
||||
var maincontent = document.getElementById('maincontent');
|
||||
if(maincontent) {
|
||||
maincontent.innerHTML = body;
|
||||
}
|
||||
}
|
||||
|
||||
function updateProgressLabels() {
|
||||
var progressBars = document.getElementsByClassName("progressBar");
|
||||
for(var i = 0; i < progressBars.length; i++) {
|
||||
var progressBar = progressBars[i];
|
||||
if(!progressBar)
|
||||
continue;
|
||||
var progressFill = progressBar.getElementsByClassName("progressFill")[0];
|
||||
if(!progressFill)
|
||||
continue;
|
||||
var width = parseInt(getComputedStyle(progressFill).width);
|
||||
var maxWidth = parseInt(getComputedStyle(progressBar).width);
|
||||
var progressLabel = progressBar.getElementsByClassName("progressLabel")[0];
|
||||
if(progressLabel)
|
||||
progressLabel.innerHTML = Math.round((width / maxWidth) * 100) + '%';
|
||||
}
|
||||
}
|
||||
|
||||
if(getComputedStyle) { setInterval(updateProgressLabels, 50); } //Fallback
|
||||
|
||||
function updateFields(json) {
|
||||
var fields = JSON.parse(json);
|
||||
for (var key in fields) {
|
||||
let value = fields[key];
|
||||
var element = document.getElementById(key);
|
||||
if(element == null) {
|
||||
continue;
|
||||
} else if(element.classList.contains('progressBar')) {
|
||||
var progressFill = element.getElementsByClassName("progressFill")[0];
|
||||
if(progressFill)
|
||||
progressFill.style["width"] = value;
|
||||
if(!getComputedStyle) { //Fallback
|
||||
var progressLabel = element.getElementsByClassName("progressLabel")[0];
|
||||
if(progressLabel)
|
||||
progressLabel.innerHTML = value;
|
||||
}
|
||||
} else {
|
||||
element.innerHTML = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -34,12 +34,12 @@ if ! { [ -x "$has_git" ] && [ -x "$has_grep" ] && [ -f "/usr/lib/i386-linux-gnu/
|
||||
if ! [ -x "$has_sudo" ]; then
|
||||
dpkg --add-architecture i386
|
||||
apt-get update
|
||||
apt-get install -y git libssl-dev:i386 grep mysql-client
|
||||
apt-get install -y git libssl-dev:i386 grep mysql-client lib32z1 pkg-config libssl-dev
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
else
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git libssl-dev:i386 grep mysql-client
|
||||
sudo apt-get install -y git libssl-dev:i386 grep mysql-client lib32z1 pkg-config libssl-dev
|
||||
sudo rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
fi
|
||||
@@ -58,10 +58,17 @@ fi
|
||||
echo "Deploying rust-g..."
|
||||
cd rust-g
|
||||
git checkout "$RUST_G_VERSION"
|
||||
~/.cargo/bin/cargo build --release
|
||||
mv target/release/librust_g.so "$1/rust_g"
|
||||
env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target=i686-unknown-linux-gnu
|
||||
mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so"
|
||||
cd ..
|
||||
|
||||
# compile tgui
|
||||
echo "Compiling tgui..."
|
||||
cd "$1"
|
||||
chmod +x tools/bootstrap/node # Workaround for https://github.com/tgstation/tgstation-server/issues/1167
|
||||
env TG_BOOTSTRAP_CACHE="$original_dir" TG_BOOTSTRAP_NODE_LINUX=1 TG_BUILD_TGS_MODE=1 tools/bootstrap/node tools/build/build.js
|
||||
cd "$original_dir"
|
||||
|
||||
if [ ! -d "../GameStaticFiles/config" ]; then
|
||||
echo "Creating initial config..."
|
||||
cp -r "$1/config" "../GameStaticFiles/config"
|
||||
@@ -76,3 +83,10 @@ if [ "$DATABASE_EXISTS" != "ss13_db" ]; then
|
||||
mysql -u root --password=$MYSQL_ROOT_PASSWORD -h mariadb -P 3306 ss13_db < "$1/$TGS_PREFIXED_SCHEMA_FILE"
|
||||
mysql -u root --password=$MYSQL_ROOT_PASSWORD -h mariadb -P 3306 ss13_db -e "INSERT INTO \`SS13_schema_revision\` (\`major\`, \`minor\`) VALUES ($TGS_SCHEMA_MAJOR_VERSION, $TGS_SCHEMA_MINOR_VERSION)"
|
||||
fi
|
||||
|
||||
# compile tgui
|
||||
echo "Compiling tgui..."
|
||||
cd "$1"
|
||||
chmod +x tools/bootstrap/node # Workaround for https://github.com/tgstation/tgstation-server/issues/1167
|
||||
chmod +x tgui/bin/tgui
|
||||
env TG_BOOTSTRAP_CACHE="$original_dir" TG_BOOTSTRAP_NODE_LINUX=1 TG_BUILD_TGS_MODE=1 tools/bootstrap/node tools/build/build.js
|
||||
@@ -6,7 +6,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Tgstation.Server.Client" Version="6.1.0" />
|
||||
<PackageReference Include="Tgstation.Server.Client" Version="8.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -18,12 +18,5 @@
|
||||
"Enable": true,
|
||||
"AllowAnyOrigin": false,
|
||||
"AllowedOrigins": null
|
||||
},
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://0.0.0.0:80"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ def update_map(map_filepath, updates, verbose=False):
|
||||
dmm_data = DMM.from_file(map_filepath)
|
||||
for update_string in updates:
|
||||
update_path(dmm_data, update_string, verbose)
|
||||
dmm_data.to_file(map_filepath, True)
|
||||
dmm_data.to_file(map_filepath)
|
||||
|
||||
|
||||
def update_all_maps(map_directory, updates, verbose=False):
|
||||
|
||||
@@ -262,8 +262,6 @@ function tag_pr($payload, $opened) {
|
||||
$tags[] = $tag;
|
||||
|
||||
check_tag_and_replace($payload, '[dnm]', 'Do Not Merge', $tags);
|
||||
if(!check_tag_and_replace($payload, '[wip]', 'Work In Progress', $tags) && check_tag_and_replace($payload, '[ready]', 'Work In Progress', $remove))
|
||||
$tags[] = 'Needs Review';
|
||||
|
||||
return array($tags, $remove);
|
||||
}
|
||||
@@ -287,81 +285,11 @@ function get_reviews($payload){
|
||||
return json_decode(github_apisend($payload['pull_request']['url'] . '/reviews'), true);
|
||||
}
|
||||
|
||||
function check_ready_for_review($payload, $labels = null, $remove = array()){
|
||||
$r4rlabel = 'Needs Review';
|
||||
$labels_which_should_not_be_ready = array('Do Not Merge', 'Work In Progress', 'Merge Conflict');
|
||||
$has_label_already = false;
|
||||
$should_not_have_label = false;
|
||||
if($labels == null)
|
||||
$labels = get_labels($payload);
|
||||
$returned = array($labels, $remove);
|
||||
//if the label is already there we may need to remove it
|
||||
foreach($labels as $L){
|
||||
if(in_array($L, $labels_which_should_not_be_ready))
|
||||
$should_not_have_label = true;
|
||||
if($L == $r4rlabel)
|
||||
$has_label_already = true;
|
||||
}
|
||||
|
||||
if($has_label_already && $should_not_have_label){
|
||||
$remove[] = $r4rlabel;
|
||||
return $returned;
|
||||
}
|
||||
|
||||
//find all reviews to see if changes were requested at some point
|
||||
$reviews = get_reviews($payload);
|
||||
|
||||
$reviews_ids_with_changes_requested = array();
|
||||
$dismissed_an_approved_review = false;
|
||||
|
||||
foreach($reviews as $R)
|
||||
if(is_maintainer($payload, $R['user']['login'])){
|
||||
$lower_state = strtolower($R['state']);
|
||||
if($lower_state == 'changes_requested')
|
||||
$reviews_ids_with_changes_requested[] = $R['id'];
|
||||
else if ($lower_state == 'approved'){
|
||||
dismiss_review($payload, $R['id'], 'Out of date review');
|
||||
$dismissed_an_approved_review = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$dismissed_an_approved_review && count($reviews_ids_with_changes_requested) == 0){
|
||||
if($has_label_already)
|
||||
$remove[] = $r4rlabel;
|
||||
return $returned; //no need to be here
|
||||
}
|
||||
|
||||
if(count($reviews_ids_with_changes_requested) > 0){
|
||||
//now get the review comments for the offending reviews
|
||||
|
||||
$review_comments = json_decode(github_apisend($payload['pull_request']['review_comments_url']), true);
|
||||
|
||||
foreach($review_comments as $C){
|
||||
//make sure they are part of an offending review
|
||||
if(!in_array($C['pull_request_review_id'], $reviews_ids_with_changes_requested))
|
||||
continue;
|
||||
|
||||
//review comments which are outdated have a null position
|
||||
if($C['position'] !== null){
|
||||
if($has_label_already)
|
||||
$remove[] = $r4rlabel;
|
||||
return $returned; //no need to tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//finally, add it if necessary
|
||||
if(!$has_label_already){
|
||||
$labels[] = $r4rlabel;
|
||||
}
|
||||
return $returned;
|
||||
}
|
||||
|
||||
function check_dismiss_changelog_review($payload){
|
||||
global $require_changelog;
|
||||
global $require_changelogs;
|
||||
global $no_changelog;
|
||||
|
||||
if(!$require_changelog)
|
||||
if(!$require_changelogs)
|
||||
return;
|
||||
|
||||
if(!$no_changelog)
|
||||
@@ -406,8 +334,6 @@ function handle_pr($payload) {
|
||||
check_dismiss_changelog_review($payload);
|
||||
case 'synchronize':
|
||||
list($labels, $remove) = tag_pr($payload, false);
|
||||
if($payload['action'] == 'synchronize')
|
||||
list($labels, $remove) = check_ready_for_review($payload, $labels, $remove);
|
||||
set_labels($payload, $labels, $remove);
|
||||
return;
|
||||
case 'reopened':
|
||||
@@ -647,15 +573,16 @@ function get_pr_code_friendliness($payload, $oldbalance = null){
|
||||
'Refactor' => 10,
|
||||
'Code Improvement' => 2,
|
||||
'Grammar and Formatting' => 1,
|
||||
'Quality of Life' => 1,
|
||||
'Priority: High' => 15,
|
||||
'Priority: CRITICAL' => 20,
|
||||
'Unit Tests' => 6,
|
||||
'Logging' => 1,
|
||||
'Feedback' => 2,
|
||||
'Performance' => 12,
|
||||
'Atomic' => 2,
|
||||
'Feature' => -10,
|
||||
'Balance/Rebalance' => -8,
|
||||
'Tweak' => -2,
|
||||
'Sound' => 1,
|
||||
'Sprites' => 1,
|
||||
'GBP: Reset' => $startingPRBalance - $oldbalance,
|
||||
@@ -676,8 +603,7 @@ function get_pr_code_friendliness($payload, $oldbalance = null){
|
||||
}
|
||||
}
|
||||
|
||||
$affecting = abs($maxNegative) >= $maxPositive ? $maxNegative : $maxPositive;
|
||||
return $affecting;
|
||||
return $maxNegative + $maxPositive;
|
||||
}
|
||||
|
||||
function is_maintainer($payload, $author){
|
||||
@@ -819,6 +745,8 @@ function checkchangelog($payload, $compile = true) {
|
||||
}
|
||||
|
||||
if (!strlen($firstword)) {
|
||||
if (count($currentchangelogblock) <= 0)
|
||||
continue;
|
||||
$currentchangelogblock[count($currentchangelogblock)-1]['body'] .= "\n";
|
||||
continue;
|
||||
}
|
||||
@@ -840,12 +768,10 @@ function checkchangelog($payload, $compile = true) {
|
||||
$currentchangelogblock[] = array('type' => 'bugfix', 'body' => $item);
|
||||
}
|
||||
break;
|
||||
case 'rsctweak':
|
||||
case 'tweaks':
|
||||
case 'tweak':
|
||||
if($item != 'tweaked a few things') {
|
||||
$tags[] = 'Tweak';
|
||||
$currentchangelogblock[] = array('type' => 'tweak', 'body' => $item);
|
||||
case 'qol':
|
||||
if($item != 'made something easier to use') {
|
||||
$tags[] = 'Quality of Life';
|
||||
$currentchangelogblock[] = array('type' => 'qol', 'body' => $item);
|
||||
}
|
||||
break;
|
||||
case 'soundadd':
|
||||
|
||||
+3
-1
@@ -32,4 +32,6 @@ cp -r strings/* $1/strings/
|
||||
#find $1/_maps -name "*.dm" -type f -delete
|
||||
|
||||
#dlls on windows
|
||||
cp *.dll $1/ || true
|
||||
if [ "$(uname -o)" = "Msys" ]; then
|
||||
cp ./*.dll $1/
|
||||
fi
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ def _self_test():
|
||||
raise
|
||||
count += 1
|
||||
|
||||
print(f"Successfully parsed {count} dmi files")
|
||||
print(f"{os.path.relpath(__file__)}: successfully parsed {count} .dmi files")
|
||||
|
||||
|
||||
def _usage():
|
||||
|
||||
+17
-22
@@ -1,41 +1,36 @@
|
||||
# Git Integration Hooks
|
||||
|
||||
This folder contains installable scripts for [Git hooks] and [merge drivers].
|
||||
This folder contains installable scripts for Git [hooks] and [merge drivers].
|
||||
Use of these hooks and drivers is optional and they must be installed
|
||||
explicitly before they take effect.
|
||||
|
||||
To install the current set of hooks, or update if new hooks are added, run
|
||||
`Install.bat` (Windows) or `tools/hooks/install` (Unix-like) as appropriate.
|
||||
`Install.bat` (Windows) or `tools/hooks/install.sh` (Unix-like) as appropriate.
|
||||
|
||||
Hooks expect a Unix-like environment on the backend. Usually this is handled
|
||||
automatically by GUI tools like TortoiseGit and GitHub for Windows, but
|
||||
[Git for Windows] is an option if you prefer to use a CLI even on Windows.
|
||||
If your Git GUI does not support a given hook, there is usually a `.bat` file
|
||||
or other script you can run instead - see the links below for details.
|
||||
|
||||
## Current Hooks
|
||||
## Hooks
|
||||
|
||||
* **Pre-commit**: Runs [mapmerge2] on changed maps, if any.
|
||||
* **DMI merger**: Attempts to [fix icon conflicts] when performing a git merge.
|
||||
If it succeeds, the file is marked merged. If it fails, it logs what states
|
||||
are still in conflict and adds them to the .dmi file, where the desired
|
||||
resolution can be chosen.
|
||||
* **Pre-commit**: Runs [mapmerge2] to reduce the diff on any changed maps.
|
||||
* **DMI merger**: Attempts to [fix icon conflicts] when performing a Git merge.
|
||||
* **DMM merger**: Attempts to [fix map conflicts] when performing a Git merge.
|
||||
|
||||
## Adding New Hooks
|
||||
|
||||
New [Git hooks] may be added by creating a file named `<hook-name>.hook` in
|
||||
New Git [hooks] may be added by creating a file named `<hook-name>.hook` in
|
||||
this directory. Git determines what hooks are available and what their names
|
||||
are. The install script copies the `.hook` file into `.git/hooks`, so editing
|
||||
the `.hook` file will require a reinstall.
|
||||
are.
|
||||
|
||||
New [merge drivers] may be added by adding a shell script named `<ext>.merge`
|
||||
New Git [merge drivers] may be added by adding a shell script named `<ext>.merge`
|
||||
and updating `.gitattributes` in the root of the repository to include the line
|
||||
`*.<ext> merge=<ext>`. The install script will set up the merge driver to point
|
||||
to the `.merge` file directly, and editing it will not require a reinstall.
|
||||
`*.<ext> merge=<ext>`.
|
||||
|
||||
`tools/hooks/python.sh` may be used as a trampoline to ensure that the correct
|
||||
version of Python is found.
|
||||
Adding or removing hooks or merge drivers requires running the install script
|
||||
again, but modifying them does not. See existing `.hook` and `.merge` files for examples.
|
||||
|
||||
[Git hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
|
||||
[hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
|
||||
[merge drivers]: https://git-scm.com/docs/gitattributes#_performing_a_three_way_merge
|
||||
[Git for Windows]: https://gitforwindows.org/
|
||||
[mapmerge2]: ../mapmerge2/README.md
|
||||
[fix icon conflicts]: ../mapmerge2/merge_driver_dmi.py
|
||||
[fix icon conflicts]: https://tgstation13.org/wiki/Resolving_icon_conflicts
|
||||
[fix map conflicts]: https://tgstation13.org/wiki/Map_Merger
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec tools/bootstrap/python -m mapmerge2.merge_driver "$@"
|
||||
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
echo Installing hooks for next time...
|
||||
call "%~dp0\..\bootstrap\python.bat" -m hooks.install
|
||||
echo.
|
||||
echo Fixing things up...
|
||||
call "%~dp0\..\bootstrap\python.bat" -m mapmerge2.fixup
|
||||
pause
|
||||
@@ -1,12 +0,0 @@
|
||||
@echo off
|
||||
cd ../../_maps/
|
||||
|
||||
for /R %%f in (*.dmm) do copy "%%f" "%%f.backup"
|
||||
|
||||
cls
|
||||
echo All dmm files in _maps directories have been backed up
|
||||
echo Now you can make your changes...
|
||||
echo ---
|
||||
echo Remember to run mapmerge.bat just before you commit your changes!
|
||||
echo ---
|
||||
pause
|
||||
+25
-16
@@ -1,8 +1,27 @@
|
||||
# Map Merge 2
|
||||
# Map Merger
|
||||
|
||||
**Map Merge 2** is an improvement over previous map merging scripts, with
|
||||
better merge-conflict prevention, multi-Z support, and automatic handling of
|
||||
key overflow. For up-to-date tips and tricks, also visit the [Map Merger] wiki article.
|
||||
The **Map Merger** is a collection of scripts that keep this repository's maps
|
||||
in a format which is easier to track in Git and less likely to cause merge
|
||||
conflicts. When merge conflicts do occur, it can sometimes resolve them.
|
||||
|
||||
For detailed troubleshooting instructions and other tips, visit the
|
||||
[Map Merger] wiki article.
|
||||
|
||||
## Installation
|
||||
|
||||
To install the [Git hooks], open the `tools/hooks/` folder and double-click
|
||||
`Install.bat`. Linux users run `tools/hooks/install.sh`.
|
||||
|
||||
## Manual Use
|
||||
|
||||
If using a Git GUI which is not compatible with the hooks:
|
||||
|
||||
* Before committing, double-click `Run Before Committing.bat`
|
||||
* When a merge has map conflicts, double-click `Resolve Map Conflicts.bat`
|
||||
|
||||
The console will show whether the operation succeeded.
|
||||
|
||||
For more details, see the [Map Merger] wiki article.
|
||||
|
||||
## What Map Merging Is
|
||||
|
||||
@@ -13,18 +32,8 @@ version of the map while maintaining all the actual changes. It requires an old
|
||||
version of the map to use as a reference and a new version of the map which
|
||||
contains the desired changes.
|
||||
|
||||
## Installation
|
||||
|
||||
To install the Git hooks, open the `tools/hooks/` folder and double-click
|
||||
`Install.bat` (Linux users run `tools/hooks/install`).
|
||||
|
||||
To use Map Merge manually, such as when using a Git GUI which is incompatible
|
||||
with some of the hooks, double-click the `.bat` files at the appropriate time.
|
||||
|
||||
A private copy of Python and any dependencies will be installed automatically.
|
||||
|
||||
For up-to-date installation and detailed troubleshooting instructions, visit
|
||||
the [Map Merger] wiki article.
|
||||
Map Merge 2 adds multi-Z support, automatic handling of key overflow, better
|
||||
merge conflict prevention, and a real merge conflict resolver.
|
||||
|
||||
## Code Structure
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
@call "%~dp0\..\bootstrap\python.bat" -m mapmerge2.merge_driver --posthoc %*
|
||||
@pause
|
||||
@@ -0,0 +1,2 @@
|
||||
@call "%~dp0\..\bootstrap\python" -m mapmerge2.precommit --use-workdir %*
|
||||
@pause
|
||||
+45
-22
@@ -22,20 +22,19 @@ class DMM:
|
||||
|
||||
@staticmethod
|
||||
def from_file(fname):
|
||||
# stream the file rather than forcing all its contents to memory
|
||||
with open(fname, 'r', encoding=ENCODING) as f:
|
||||
return _parse(iter(lambda: f.read(1), ''))
|
||||
return _parse(f.read())
|
||||
|
||||
@staticmethod
|
||||
def from_bytes(bytes):
|
||||
return _parse(bytes.decode(ENCODING))
|
||||
|
||||
def to_file(self, fname, tgm = True):
|
||||
def to_file(self, fname, *, tgm = True):
|
||||
self._presave_checks()
|
||||
with open(fname, 'w', newline='\n', encoding=ENCODING) as f:
|
||||
(save_tgm if tgm else save_dmm)(self, f)
|
||||
|
||||
def to_bytes(self, tgm = True):
|
||||
def to_bytes(self, *, tgm = True):
|
||||
self._presave_checks()
|
||||
bio = io.BytesIO()
|
||||
with io.TextIOWrapper(bio, newline='\n', encoding=ENCODING) as f:
|
||||
@@ -43,20 +42,29 @@ class DMM:
|
||||
f.flush()
|
||||
return bio.getvalue()
|
||||
|
||||
def get_or_generate_key(self, tile):
|
||||
try:
|
||||
return self.dictionary.inv[tile]
|
||||
except KeyError:
|
||||
key = self.generate_new_key()
|
||||
self.dictionary[key] = tile
|
||||
return key
|
||||
|
||||
def get_tile(self, coord):
|
||||
return self.dictionary[self.grid[coord]]
|
||||
|
||||
def set_tile(self, coord, tile):
|
||||
tile = tuple(tile)
|
||||
self.grid[coord] = self.get_or_generate_key(tile)
|
||||
|
||||
def generate_new_key(self):
|
||||
free_keys = self._ensure_free_keys(1)
|
||||
max_key = max_key_for(self.key_length)
|
||||
# choose one of the free keys at random
|
||||
key = 0
|
||||
while free_keys:
|
||||
if key not in self.dictionary:
|
||||
# this construction is used to avoid needing to construct the
|
||||
# full set in order to random.choice() from it
|
||||
if random.random() < 1 / free_keys:
|
||||
return key
|
||||
free_keys -= 1
|
||||
key += 1
|
||||
|
||||
raise RuntimeError("ran out of keys, this shouldn't happen")
|
||||
key = random.randint(0, max_key - 1)
|
||||
while key in self.dictionary:
|
||||
key = random.randint(0, max_key - 1)
|
||||
return key
|
||||
|
||||
def overwrite_key(self, key, fixed, bad_keys):
|
||||
try:
|
||||
@@ -125,6 +133,9 @@ class DMM:
|
||||
for x in range(1, self.size.x + 1):
|
||||
yield (y, x)
|
||||
|
||||
def __repr__(self):
|
||||
return f"DMM(size={self.size}, key_length={self.key_length}, dictionary_size={len(self.dictionary)})"
|
||||
|
||||
# ----------
|
||||
# key handling
|
||||
|
||||
@@ -227,10 +238,8 @@ def is_bad_atom_ordering(key, atoms):
|
||||
print(f"Warning: key '{key}' is missing either a turf or area")
|
||||
return can_fix
|
||||
|
||||
def fix_atom_ordering(atoms):
|
||||
movables = []
|
||||
turfs = []
|
||||
areas = []
|
||||
def split_atom_groups(atoms):
|
||||
movables, turfs, areas = [], [], []
|
||||
for each in atoms:
|
||||
if each.startswith('/turf'):
|
||||
turfs.append(each)
|
||||
@@ -238,6 +247,10 @@ def fix_atom_ordering(atoms):
|
||||
areas.append(each)
|
||||
else:
|
||||
movables.append(each)
|
||||
return movables, turfs, areas
|
||||
|
||||
def fix_atom_ordering(atoms):
|
||||
movables, turfs, areas = split_atom_groups(atoms)
|
||||
movables.extend(turfs)
|
||||
movables.extend(areas)
|
||||
return movables
|
||||
@@ -287,7 +300,7 @@ def save_tgm(dmm, output):
|
||||
output.write("\n")
|
||||
for x in range(1, max_x + 1):
|
||||
output.write(f"({x},{1},{z}) = {{\"\n")
|
||||
for y in range(1, max_y + 1):
|
||||
for y in range(max_y, 0, -1):
|
||||
output.write(f"{num_to_key(dmm.grid[x, y, z], dmm.key_length)}\n")
|
||||
output.write("\"}\n")
|
||||
|
||||
@@ -309,7 +322,7 @@ def save_dmm(dmm, output):
|
||||
for z in range(1, max_z + 1):
|
||||
output.write(f"(1,1,{z}) = {{\"\n")
|
||||
|
||||
for y in range(1, max_y + 1):
|
||||
for y in range(max_y, 0, -1):
|
||||
for x in range(1, max_x + 1):
|
||||
try:
|
||||
output.write(num_to_key(dmm.grid[x, y, z], dmm.key_length))
|
||||
@@ -542,7 +555,17 @@ def _parse(map_raw_text):
|
||||
if curr_y > maxy:
|
||||
maxy = curr_y
|
||||
|
||||
if not grid:
|
||||
# Usually caused by unbalanced quotes.
|
||||
max_key = num_to_key(max(dictionary.keys()), key_length, True)
|
||||
raise ValueError(f"dmm failed to parse, check for a syntax error near or after key {max_key!r}")
|
||||
|
||||
# Convert from raw .dmm coordinates to DM/BYOND coordinates by flipping Y
|
||||
grid2 = dict()
|
||||
for (x, y, z), tile in grid.items():
|
||||
grid2[x, maxy + 1 - y, z] = tile
|
||||
|
||||
data = DMM(key_length, Coordinate(maxx, maxy, maxz))
|
||||
data.dictionary = dictionary
|
||||
data.grid = grid
|
||||
data.grid = grid2
|
||||
return data
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
@echo off
|
||||
set MAPROOT=%~dp0/../../_maps/
|
||||
set TGM=1
|
||||
call "%~dp0\..\bootstrap\python" -m mapmerge2.convert %*
|
||||
pause
|
||||
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import sys
|
||||
from .dmm import *
|
||||
|
||||
|
||||
def _self_test():
|
||||
# test: can we load every DMM in the tree
|
||||
count = 0
|
||||
for dirpath, dirnames, filenames in os.walk('.'):
|
||||
if '.git' in dirnames:
|
||||
dirnames.remove('.git')
|
||||
for filename in filenames:
|
||||
if filename.endswith('.dmm'):
|
||||
fullpath = os.path.join(dirpath, filename)
|
||||
try:
|
||||
DMM.from_file(fullpath)
|
||||
except Exception:
|
||||
print('Failed on:', fullpath)
|
||||
raise
|
||||
count += 1
|
||||
|
||||
print(f"{os.path.relpath(__file__)}: successfully parsed {count} .dmm files")
|
||||
|
||||
|
||||
def _usage():
|
||||
print(f"Usage:")
|
||||
print(f" tools{os.sep}bootstrap{os.sep}python -m {__spec__.name}")
|
||||
exit(1)
|
||||
|
||||
|
||||
def _main():
|
||||
if len(sys.argv) == 1:
|
||||
return _self_test()
|
||||
|
||||
return _usage()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_main()
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import pygit2
|
||||
from . import dmm
|
||||
from .mapmerge import merge_map
|
||||
|
||||
|
||||
STATUS_INDEX = (pygit2.GIT_STATUS_INDEX_NEW
|
||||
| pygit2.GIT_STATUS_INDEX_MODIFIED
|
||||
| pygit2.GIT_STATUS_INDEX_DELETED
|
||||
| pygit2.GIT_STATUS_INDEX_RENAMED
|
||||
| pygit2.GIT_STATUS_INDEX_TYPECHANGE
|
||||
)
|
||||
STATUS_WT = (pygit2.GIT_STATUS_WT_NEW
|
||||
| pygit2.GIT_STATUS_WT_MODIFIED
|
||||
| pygit2.GIT_STATUS_WT_DELETED
|
||||
| pygit2.GIT_STATUS_WT_RENAMED
|
||||
| pygit2.GIT_STATUS_WT_TYPECHANGE
|
||||
)
|
||||
ABBREV_LEN = 12
|
||||
TGM_HEADER = dmm.TGM_HEADER.encode(dmm.ENCODING)
|
||||
|
||||
|
||||
def walk_tree(tree, *, _prefix=''):
|
||||
for child in tree:
|
||||
if isinstance(child, pygit2.Tree):
|
||||
yield from walk_tree(child, _prefix=f'{_prefix}{child.name}/')
|
||||
else:
|
||||
yield f'{_prefix}{child.name}', child
|
||||
|
||||
|
||||
def insert_into_tree(repo, tree_builder, path, blob_oid):
|
||||
try:
|
||||
first, rest = path.split('/', 1)
|
||||
except ValueError:
|
||||
tree_builder.insert(path, blob_oid, pygit2.GIT_FILEMODE_BLOB)
|
||||
else:
|
||||
inner = repo.TreeBuilder(tree_builder.get(first))
|
||||
insert_into_tree(repo, inner, rest, blob_oid)
|
||||
tree_builder.insert(first, inner.write(), pygit2.GIT_FILEMODE_TREE)
|
||||
|
||||
|
||||
def main(repo):
|
||||
if repo.index.conflicts:
|
||||
print("You need to resolve merge conflicts first.")
|
||||
return 1
|
||||
|
||||
# Ensure the index is clean.
|
||||
for path, status in repo.status().items():
|
||||
if status & pygit2.GIT_STATUS_IGNORED:
|
||||
continue
|
||||
if status & STATUS_INDEX:
|
||||
print("You have changes staged for commit. Commit them or unstage them first.")
|
||||
print("If you are about to commit maps for the first time, run `Run Before Committing.bat`.")
|
||||
return 1
|
||||
if path.endswith(".dmm") and (status & STATUS_WT):
|
||||
print("You have modified maps. Commit them first.")
|
||||
print("If you are about to commit maps for the first time, run `Run Before Committing.bat`.")
|
||||
return 1
|
||||
|
||||
# Read the HEAD commit.
|
||||
head_commit = repo[repo.head.target]
|
||||
head_files = {}
|
||||
for path, blob in walk_tree(head_commit.tree):
|
||||
if path.endswith(".dmm"):
|
||||
data = blob.read_raw()
|
||||
if not data.startswith(TGM_HEADER):
|
||||
head_files[path] = dmm.DMM.from_bytes(data)
|
||||
|
||||
if not head_files:
|
||||
print("All committed maps appear to be in the correct format.")
|
||||
print("If you are about to commit maps for the first time, run `Run Before Committing.bat`.")
|
||||
return 1
|
||||
|
||||
# Work backwards to find a base for each map, converting as found.
|
||||
converted = {}
|
||||
if len(head_commit.parents) != 1:
|
||||
print("Unable to automatically fix anything because HEAD is a merge commit.")
|
||||
return 1
|
||||
commit_message_lines = []
|
||||
working_commit = head_commit.parents[0]
|
||||
while len(converted) < len(head_files):
|
||||
for path in head_files.keys() - converted.keys():
|
||||
try:
|
||||
blob = working_commit.tree[path]
|
||||
except KeyError:
|
||||
commit_message_lines.append(f"{'new':{ABBREV_LEN}}: {path}")
|
||||
print(f"Converting new map: {path}")
|
||||
converted[path] = head_files[path]
|
||||
else:
|
||||
data = blob.read_raw()
|
||||
if data.startswith(TGM_HEADER):
|
||||
str_id = str(working_commit.id)[:ABBREV_LEN]
|
||||
commit_message_lines.append(f"{str_id}: {path}")
|
||||
print(f"Converting map: {path}")
|
||||
converted[path] = merge_map(head_files[path], dmm.DMM.from_bytes(data))
|
||||
if len(working_commit.parents) != 1:
|
||||
print("A merge commit was encountered before good versions of these maps were found:")
|
||||
print("\n".join(f" {x}" for x in head_files.keys() - base_files.keys()))
|
||||
return 1
|
||||
working_commit = working_commit.parents[0]
|
||||
|
||||
# Okay, do the actual work.
|
||||
tree_builder = repo.TreeBuilder(head_commit.tree)
|
||||
for path, merged_map in converted.items():
|
||||
blob_oid = repo.create_blob(merged_map.to_bytes())
|
||||
insert_into_tree(repo, tree_builder, path, blob_oid)
|
||||
repo.index.add(pygit2.IndexEntry(path, blob_oid, repo.index[path].mode))
|
||||
merged_map.to_file(os.path.join(repo.workdir, path))
|
||||
|
||||
# Save the index.
|
||||
repo.index.write()
|
||||
|
||||
# Commit the index to the current branch.
|
||||
signature = pygit2.Signature(repo.config['user.name'], repo.config['user.email'])
|
||||
joined = "\n".join(commit_message_lines)
|
||||
repo.create_commit(
|
||||
repo.head.name,
|
||||
signature, # author
|
||||
signature, # committer
|
||||
f'Convert maps to TGM\n\n{joined}\n\nAutomatically commited by: {os.path.relpath(__file__, repo.workdir)}',
|
||||
tree_builder.write(),
|
||||
[head_commit.id],
|
||||
)
|
||||
|
||||
# Success.
|
||||
print("Successfully committed a fixup. Push as needed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main(pygit2.Repository(pygit2.discover_repository(os.getcwd()))))
|
||||
@@ -1,5 +0,0 @@
|
||||
@echo off
|
||||
set MAPROOT=%~dp0/../../_maps/
|
||||
set TGM=1
|
||||
call "%~dp0\..\bootstrap\python" -m mapmerge2.mapmerge %*
|
||||
pause
|
||||
@@ -66,7 +66,7 @@ def merge_map(new_map, old_map, delete_unused=False):
|
||||
|
||||
# step two: delete unused keys
|
||||
if unused_keys:
|
||||
print(f"Notice: Trimming {len(unused_keys)} unused dictionary keys.")
|
||||
#print(f"Notice: Trimming {len(unused_keys)} unused dictionary keys.")
|
||||
for key in unused_keys:
|
||||
del merged.dictionary[key]
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import collections
|
||||
from . import dmm, mapmerge
|
||||
from hooks.merge_frontend import MergeDriver
|
||||
|
||||
|
||||
debug_stats = collections.defaultdict(int)
|
||||
|
||||
|
||||
def select(base, left, right, *, debug=None):
|
||||
if left == right:
|
||||
# whether or not it's in the base, both sides agree
|
||||
if debug:
|
||||
debug_stats[f"select {debug} both"] += 1
|
||||
return left
|
||||
elif base == left:
|
||||
# base == left, but right is different: accept right
|
||||
if debug:
|
||||
debug_stats[f"select {debug} right"] += 1
|
||||
return right
|
||||
elif base == right:
|
||||
# base == right, but left is different: accept left
|
||||
if debug:
|
||||
debug_stats[f"select {debug} left"] += 1
|
||||
return left
|
||||
else:
|
||||
# all three versions are different
|
||||
if debug:
|
||||
debug_stats[f"select {debug} fail"] += 1
|
||||
return None
|
||||
|
||||
|
||||
def three_way_merge(base, left, right):
|
||||
if base.size != left.size or base.size != right.size:
|
||||
print("Dimensions have changed:")
|
||||
print(f" Base: {base.size}")
|
||||
print(f" Ours: {left.size}")
|
||||
print(f" Theirs: {right.size}")
|
||||
return True, None
|
||||
|
||||
trouble = False
|
||||
merged = dmm.DMM(base.key_length, base.size)
|
||||
merged.dictionary = base.dictionary.copy()
|
||||
|
||||
for (z, y, x) in base.coords_zyx:
|
||||
coord = x, y, z
|
||||
base_tile = base.get_tile(coord)
|
||||
left_tile = left.get_tile(coord)
|
||||
right_tile = right.get_tile(coord)
|
||||
|
||||
# try to merge the whole tiles
|
||||
whole_tile_merge = select(base_tile, left_tile, right_tile, debug='tile')
|
||||
if whole_tile_merge is not None:
|
||||
merged.set_tile(coord, whole_tile_merge)
|
||||
continue
|
||||
|
||||
# try to merge each group independently (movables, turfs, areas)
|
||||
base_movables, base_turfs, base_areas = dmm.split_atom_groups(base_tile)
|
||||
left_movables, left_turfs, left_areas = dmm.split_atom_groups(left_tile)
|
||||
right_movables, right_turfs, right_areas = dmm.split_atom_groups(right_tile)
|
||||
|
||||
merged_movables = select(base_movables, left_movables, right_movables, debug='movable')
|
||||
merged_turfs = select(base_turfs, left_turfs, right_turfs, debug='turf')
|
||||
merged_areas = select(base_areas, left_areas, right_areas, debug='area')
|
||||
|
||||
if merged_movables is not None and merged_turfs is not None and merged_areas is not None:
|
||||
merged.set_tile(coord, merged_movables + merged_turfs + merged_areas)
|
||||
continue
|
||||
|
||||
# TODO: more advanced strategies?
|
||||
|
||||
# fall back to requiring manual conflict resolution
|
||||
trouble = True
|
||||
print(f" C: Both sides touch the tile at {coord}")
|
||||
|
||||
if merged_movables is None:
|
||||
obj_name = "---Merge conflict marker---"
|
||||
merged_movables = left_movables + [f'/obj{{name = "{obj_name}"}}'] + right_movables
|
||||
print(f" Left and right movable groups are split by an `/obj` named \"{obj_name}\"")
|
||||
if merged_turfs is None:
|
||||
merged_turfs = left_turfs
|
||||
print(f" Saving turf: {', '.join(left_turfs)}")
|
||||
print(f" Alternative: {', '.join(right_turfs)}")
|
||||
print(f" Original: {', '.join(base_turfs)}")
|
||||
if merged_areas is None:
|
||||
merged_areas = left_areas
|
||||
print(f" Saving area: {', '.join(left_areas)}")
|
||||
print(f" Alternative: {', '.join(right_areas)}")
|
||||
print(f" Original: {', '.join(base_areas)}")
|
||||
|
||||
merged.set_tile(coord, merged_movables + merged_turfs + merged_areas)
|
||||
|
||||
merged = mapmerge.merge_map(merged, base)
|
||||
return trouble, merged
|
||||
|
||||
|
||||
class DmmDriver(MergeDriver):
|
||||
driver_id = 'dmm'
|
||||
|
||||
def merge(self, base, left, right):
|
||||
map_base = dmm.DMM.from_bytes(base.read())
|
||||
map_left = dmm.DMM.from_bytes(left.read())
|
||||
map_right = dmm.DMM.from_bytes(right.read())
|
||||
trouble, merge_result = three_way_merge(map_base, map_left, map_right)
|
||||
return not trouble, merge_result
|
||||
|
||||
def to_file(self, outfile, merge_result):
|
||||
outfile.write(merge_result.to_bytes())
|
||||
|
||||
def post_announce(self, success, merge_result):
|
||||
if not success:
|
||||
print("!!! Manual merge required!")
|
||||
if merge_result:
|
||||
print(" A best-effort merge was performed. You must edit the map and confirm")
|
||||
print(" that all coordinates mentioned above are as desired.")
|
||||
else:
|
||||
print(" The map was totally unable to be merged; you must start with one version")
|
||||
print(" or the other and manually resolve the conflict. Information about the")
|
||||
print(" conflicting tiles is listed above.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(DmmDriver().main())
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import pygit2
|
||||
from . import dmm
|
||||
from .mapmerge import merge_map
|
||||
|
||||
def main(repo):
|
||||
|
||||
def main(repo, *, use_workdir=False):
|
||||
if repo.index.conflicts:
|
||||
print("You need to resolve merge conflicts first.")
|
||||
return 1
|
||||
@@ -16,12 +18,21 @@ def main(repo):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
target_statuses = pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_INDEX_NEW
|
||||
skip_to_file_statuses = pygit2.GIT_STATUS_WT_DELETED | pygit2.GIT_STATUS_WT_MODIFIED
|
||||
if use_workdir:
|
||||
target_statuses |= pygit2.GIT_STATUS_WT_MODIFIED | pygit2.GIT_STATUS_WT_NEW
|
||||
skip_to_file_statuses &= ~pygit2.GIT_STATUS_WT_MODIFIED
|
||||
|
||||
changed = 0
|
||||
for path, status in repo.status().items():
|
||||
if path.endswith(".dmm") and (status & (pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_INDEX_NEW)):
|
||||
if path.endswith(".dmm") and (status & target_statuses):
|
||||
# read the index
|
||||
index_entry = repo.index[path]
|
||||
index_map = dmm.DMM.from_bytes(repo[index_entry.id].read_raw())
|
||||
if use_workdir:
|
||||
index_map = dmm.DMM.from_file(os.path.join(repo.workdir, path))
|
||||
else:
|
||||
index_map = dmm.DMM.from_bytes(repo[index_entry.id].read_raw())
|
||||
|
||||
try:
|
||||
head_blob = repo[repo[repo.head.target].tree[path].id]
|
||||
@@ -32,7 +43,7 @@ def main(repo):
|
||||
merged_map = index_map
|
||||
else:
|
||||
# Entry in HEAD, merge the index over it
|
||||
print(f"Merging map: {path}", flush=True)
|
||||
print(f"Converting map: {path}", flush=True)
|
||||
assert not (status & pygit2.GIT_STATUS_INDEX_NEW)
|
||||
head_map = dmm.DMM.from_bytes(head_blob.read_raw())
|
||||
merged_map = merge_map(index_map, head_map)
|
||||
@@ -43,15 +54,16 @@ def main(repo):
|
||||
changed += 1
|
||||
|
||||
# write to the working directory if that's clean
|
||||
if status & (pygit2.GIT_STATUS_WT_DELETED | pygit2.GIT_STATUS_WT_MODIFIED):
|
||||
if status & skip_to_file_statuses:
|
||||
print(f"Warning: {path} has unindexed changes, not overwriting them")
|
||||
else:
|
||||
merged_map.to_file(os.path.join(repo.workdir, path))
|
||||
|
||||
if changed:
|
||||
repo.index.write()
|
||||
print(f"Merged {changed} maps.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main(pygit2.Repository(pygit2.discover_repository(os.getcwd()))))
|
||||
repo = pygit2.Repository(pygit2.discover_repository(os.getcwd()))
|
||||
exit(main(repo, use_workdir='--use-workdir' in sys.argv))
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
@echo off
|
||||
set MAPROOT=%~dp0/../../_maps/
|
||||
set TGM=0
|
||||
call "%~dp0\..\bootstrap\python" -m mapmerge2.convert %*
|
||||
pause
|
||||
Reference in New Issue
Block a user