hmmlib JavaScript port

This commit is contained in:
Alex Baines 2017-06-19 01:17:56 +01:00
parent 3749294def
commit 648e3b29fe
3 changed files with 1068 additions and 0 deletions

810
hmmlib-js/hmmlib.js Normal file
View File

@ -0,0 +1,810 @@
function HMML_parse(contents) {
var l = new HMMLexer();
var state = {
line: 0,
annos: [],
meta: {
member: "",
twitch: "",
project: "",
title: "",
platform: "",
id: "",
annotator: ""
},
an: {
line: 0,
time: "",
text: "",
author: "",
references: [],
markers: [],
quote: {
id: 0,
author: ""
},
is_quote: false
},
ref: {
site: "",
page: "",
url: "",
title: "",
article: "",
author: "",
editor: "",
publisher: "",
isbn: "",
offset: 0
},
error: {
line: 0,
msg: ""
},
attr: "",
mnext: 0,
first: true
};
l.setInput(contents, state);
try {
var r;
do {
r = l.next();
} while (!r);
state.annos.forEach(function(a) {
if (!a.is_quote) {
delete a.quote;
}
if (a.author === "") delete a.author;
delete a.is_quote;
});
return {
metadata: state.meta,
annotations: state.annos
};
} catch (e) {
return {
error: state.error
};
}
}
function HMMLexer() {}
/* generated by jison-lex 0.3.4 */
HMMLexer.prototype = {
EOF: 1,
parseError: function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
// resets the lexer, sets new input
setInput: function(input, yy) {
this.yy = yy || this.yy || {};
this._input = input;
this._more = this._backtrack = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {
first_line: 1,
first_column: 0,
last_line: 1,
last_column: 0
};
if (this.options.ranges) {
this.yylloc.range = [0, 0];
}
this.offset = 0;
return this;
},
// consumes and returns one char from the input
input: function() {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) {
this.yylloc.range[1]++;
}
this._input = this._input.slice(1);
return ch;
},
// unshifts one char (or a string) into the input
unput: function(ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length - len);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length - 1);
this.matched = this.matched.substr(0, this.matched.length - 1);
if (lines.length - 1) {
this.yylineno -= lines.length - 1;
}
var r = this.yylloc.range;
this.yylloc = {
first_line: this.yylloc.first_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0) +
oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
this.yyleng = this.yytext.length;
return this;
},
// When called from action, caches matched text and appends it on next action
more: function() {
this._more = true;
return this;
},
// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
reject: function() {
if (this.options.backtrack_lexer) {
this._backtrack = true;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
return this;
},
// retain first n characters of the match
less: function(n) {
this.unput(this.match.slice(n));
},
// displays already matched input, i.e. for error messages
pastInput: function() {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, "");
},
// displays upcoming input, i.e. for error messages
upcomingInput: function() {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20 - next.length);
}
return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
},
// displays the character position where the lexing error occurred, i.e. for error messages
showPosition: function() {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c + "^";
},
// test the lexed token: return FALSE when not a match, otherwise return token
test_match: function(match, indexed_rule) {
var token,
lines,
backup;
if (this.options.backtrack_lexer) {
// save context
backup = {
yylineno: this.yylineno,
yylloc: {
first_line: this.yylloc.first_line,
last_line: this.last_line,
first_column: this.yylloc.first_column,
last_column: this.yylloc.last_column
},
yytext: this.yytext,
match: this.match,
matches: this.matches,
matched: this.matched,
yyleng: this.yyleng,
offset: this.offset,
_more: this._more,
_input: this._input,
yy: this.yy,
conditionStack: this.conditionStack.slice(0),
done: this.done
};
if (this.options.ranges) {
backup.yylloc.range = this.yylloc.range.slice(0);
}
}
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno += lines.length;
}
this.yylloc = {
first_line: this.yylloc.last_line,
last_line: this.yylineno + 1,
first_column: this.yylloc.last_column,
last_column: lines ?
lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length
};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._backtrack = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
if (this.done && this._input) {
this.done = false;
}
if (token) {
return token;
} else if (this._backtrack) {
// recover context
for (var k in backup) {
this[k] = backup[k];
}
return false; // rule action called reject() implying the next rule should be tested instead.
}
return false;
},
// return next match in input
next: function() {
if (this.done) {
return this.EOF;
}
if (!this._input) {
this.done = true;
}
var token,
match,
tempMatch,
index;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i = 0; i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (this.options.backtrack_lexer) {
token = this.test_match(tempMatch, rules[i]);
if (token !== false) {
return token;
} else if (this._backtrack) {
match = false;
continue; // rule action called reject() implying a rule MISmatch.
} else {
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
} else if (!this.options.flex) {
break;
}
}
}
if (match) {
token = this.test_match(match, rules[index]);
if (token !== false) {
return token;
}
// else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
return false;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
text: "",
token: null,
line: this.yylineno
});
}
},
// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
begin: function begin(condition) {
this.conditionStack.push(condition);
},
// pop the previously active lexer condition state off the condition stack
popState: function popState() {
var n = this.conditionStack.length - 1;
if (n > 0) {
return this.conditionStack.pop();
} else {
return this.conditionStack[0];
}
},
// produce the lexer rule set which is active for the currently active lexer condition state
_currentRules: function _currentRules() {
if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
} else {
return this.conditions["INITIAL"].rules;
}
},
// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
topState: function topState(n) {
n = this.conditionStack.length - 1 - Math.abs(n || 0);
if (n >= 0) {
return this.conditionStack[n];
} else {
return "INITIAL";
}
},
// alias for begin(condition)
pushState: function pushState(condition) {
this.begin(condition);
},
// return the number of states currently on the stack
stateStackSize: function stateStackSize() {
return this.conditionStack.length;
},
options: {
"moduleName": "test"
},
performAction: function anonymous(yy, yy_, $avoiding_name_collisions, YY_START) {
function ERR(yy, err) {
console.log(err);
yy.error.line = yy.line;
yy.error.msg = err;
throw "tantrum";
}
function CHECKESCAPE(yy, str) {
if (!"[]:@~\\\"".find(str)) {
ERR(yy, "hmmlib: Unknown backslash escape code: %s", str);
}
}
function UNQUOTE(yy, str) {
var i = 0;
var j = 0;
while ((j = str.indexOf('\\', i)) != -1) {
CHECKESCAPE(yy, str.charAt(j));
str = str.slice(i, j) + str.slice(j + 1);
i = j + 1;
}
return str;
}
function M_(yy, str, s) {
yy.an.markers.push({
type: str
});
yy.mnext = s;
}
function M_ADD(yy, t, n) {
yy.an.markers[yy.an.markers.length - 1].marker = t.substr(0, n);
yy.an.markers[yy.an.markers.length - 1].offset = t.length;
yy.an.text += t.substr(0, n);
}
function MX_ADD(yy, c) {
yy.an.markers[yy.an.markers.length - 1].parameter += c;
yy.an.text += c;
}
function NEWANNO(yy) {
if (!yy.first) yy.annos.push(yy.an);
yy.an = {
line: yy.line,
time: "",
text: "",
author: "",
references: [],
markers: [],
quote: {
id: 0,
author: ""
},
is_quote: false
};
yy.an.line = yy.line;
yy.first = false;
}
var YYSTATE = YY_START;
switch ($avoiding_name_collisions) {
case 0:
ERR(yy, "Unexpected EOF, video close tag not found.");
break;
case 1:
yy.line++;
break;
case 2:
yy_.begin("VIDEO");
break;
case 3:
ERR(yy, "Missing video tag.");
break;
case 4:
break;
case 5:
yy.attr = "member";
yy_.begin("V_ATTR");
break;
case 6:
yy.attr = "twitch";
yy_.begin("V_ATTR");
break;
case 7:
yy.attr = "project";
yy_.begin("V_ATTR");
break;
case 8:
yy.attr = "title";
yy_.begin("V_ATTR");
break;
case 9:
yy.attr = "platform";
yy_.begin("V_ATTR");
break;
case 10:
yy.attr = "id";
yy_.begin("V_ATTR");
break;
case 11:
yy.attr = "annotator";
yy_.begin("V_ATTR");
break;
case 12:
yy_.begin("ANNOTATION");
break;
case 13:
ERR(yy, "Invalid char '" + yy_.yytext + "' in video tag.");
break;
case 14:
yy_.begin("VIDEO");
break;
case 15:
yy.meta[yy.attr] = yy_.yytext;
yy_.begin("VIDEO");
break;
case 16:
yy.meta[yy.attr] = UNQUOTE(yy, yy_.yytext.substr(1, yy_.yyleng - 2));
yy_.begin("VIDEO");
break;
case 17:
yy_.less(0);
yy_.begin("VIDEO");
break;
case 18:
NEWANNO(yy);
yy.an.time = yy_.yytext.substr(1, yy_.yyleng - 4);
yy_.begin("AUTHOR");
break;
case 19:
NEWANNO(yy);
yy.an.time = yy_.yytext.substr(1, yy_.yyleng - 2);
yy_.begin("TEXT_START");
break;
case 20:
ERR(yy, "Timecode '" + yy_.yytext + "' out of range.");
break;
case 21:
break;
case 22:
ERR(yy, "Cannot parse annotation. Expected timecode.");
break;
case 23:
M_(yy, "CATEGORY", "TEXT");
yy_.begin("MARKER");
break;
case 24:
M_(yy, "MEMBER", "TEXT");
yy_.begin("MARKER");
break;
case 25:
M_(yy, "PROJECT", "TEXT");
yy_.begin("MARKER");
break;
case 26:
yy_.less(0);
yy_.begin("TEXT");
break;
case 27:
ERR(yy, "Unknown character '" + yy_.yytext + "' after timecode.");
break;
case 28:
yy.an.text += yy_.yytext;
break;
case 29:
CHECKESCAPE(yy, yy_.yytext.charAt(1));
yy.an.text += yy_.yytext.substr(1, yy_.yyleng - 1);
break;
case 30:
yy.an.text += ' ';
M_(yy, "CATEGORY", "TEXT");
yy_.begin("MARKER");
break;
case 31:
yy.an.text += ' ';
M_(yy, "MEMBER", "TEXT");
yy_.begin("MARKER");
break;
case 32:
yy.an.text += ' ';
M_(yy, "PROJECT", "TEXT");
yy_.begin("MARKER");
break;
case 33:
M_(yy, "CATEGORY", "MARKER_XTRA");
yy_.begin("MARKER");
break;
case 34:
M_(yy, "MEMBER", "MARKER_XTRA");
yy_.begin("MARKER");
break;
case 35:
M_(yy, "PROJECT", "MARKER_XTRA");
yy_.begin("MARKER");
break;
case 36:
yy_.begin("AFTERTEXT");
break;
case 37:
yy.ref.offset = yy.an.text.length;
yy_.begin("REF");
break;
case 38:
break;
case 39:
yy.an.text += ' ';
break;
case 40:
yy.an.text += yy_.yytext;
break;
case 41:
M_ADD(yy, yy_.yytext);
yy_.begin(yy.mnext);
break;
case 42:
M_ADD(yy, yy_.yytext.substr(1, yy_.yyleng - 2));
yy_.begin(yy.mnext);
break;
case 43:
ERR(yy, "Cannot parse Marker. Expected quoted or alphanumeric attribute.");
break;
case 44:
MX_ADD(yy, ']');
break;
case 45:
yy_.begin("TEXT");
break;
case 46:
if (yy.an.markers[yy.an.markers.length - 1].parameter) {
MX_ADD(yy, ' ');
}
break;
case 47:
MX_ADD(yy, yy_.yytext);
break;
case 48:
break;
case 49:
yy.attr = "site";
yy_.begin("R_ATTR");
break;
case 50:
yy.attr = "page";
yy_.begin("R_ATTR");
break;
case 51:
yy.attr = "url";
yy_.begin("R_ATTR");
break;
case 52:
yy.attr = "title";
yy_.begin("R_ATTR");
break;
case 53:
yy.attr = "article";
yy_.begin("R_ATTR");
break;
case 54:
yy.attr = "author";
yy_.begin("R_ATTR");
break;
case 55:
yy.attr = "editor";
yy_.begin("R_ATTR");
break;
case 56:
yy.attr = "publisher";
yy_.begin("R_ATTR");
break;
case 57:
yy.attr = "isbn";
yy_.begin("R_ATTR");
break;
case 58:
yy.an.references.push(yy.ref);
yy.ref = {};
yy_.begin("TEXT");
break;
case 59:
ERR(yy, "Unexpected item in ref: " + yy_.yytext);
break;
case 60:
break;
case 61:
yy.ref[yy.attr] = yy_.yytext;
yy_.begin("REF");
break;
case 62:
yy.ref[yy.attr] = UNQUOTE(yy, yy_.yytext.substr(1, yy_.yyleng - 2));
yy_.begin("REF");
break;
case 63:
NEWANNO(yy);
return 1;
break;
case 64:
break;
case 65:
yy_.begin("QUOTES");
break;
case 66:
yy_.begin("CATEGORIES");
yy_.less(1);
break;
case 67:
yy_.begin("ANNOTATION");
yy_.less(0);
break;
case 68:
ERR(yy, "Unexpected thing after text node: " + yy_.yytext);
break;
case 69:
ERR(yy, "Unexpected thing after text node: " + yy_.yytext);
break;
case 70:
yy.an.author = yy_.yytext.substr(0, yy_.yyleng - 1);
yy_.begin("TEXT_START");
break;
case 71:
break;
case 72:
break;
case 73:
yy.an.markers.push({
type: "HMML_CATEGORY",
marker: yy_.yytext.substr(1, yy_.yyleng - 1),
offset: -1
});
break;
case 74:
yy.an.markers.push({
type: "HMML_CATEGORY",
marker: UNQUOTE(yy, yy_.yytext.substr(2, yy_.yyleng - 3)),
offset: -1
});
break;
case 75:
yy_.begin("QUOTES");
break;
case 76:
yy_.begin("ANNOTATION");
break;
case 77:
ERR(yy, "Unexpected character in category tag: " + yy_.yytext);
break;
case 78:
break;
case 79:
yy.an.is_quote = true;
yy.an.quote.id = parseInt(yy_.yytext);
yy_.begin("ANNOTATION");
break;
case 80:
yy.an.quote.author += yy_.yytext;
break;
case 81:
ERR(yy, "Unexpected character in quotes tag: " + yy_.yytext);
break;
}
},
rules: [/^(?:$)/, /^(?:\r\n|\n)/, /^(?:(\[video\b))/, /^(?:.)/, /^(?:([\t \r]+))/, /^(?:member([\t \r]*)=)/, /^(?:twitch_username([\t \r]*)=)/, /^(?:project([\t \r]*)=)/, /^(?:title([\t \r]*)=)/, /^(?:platform([\t \r]*)=)/, /^(?:id([\t \r]*)=)/, /^(?:annotator([\t \r]*)=)/, /^(?:\])/, /^(?:.)/, /^(?:([\t \r]+))/, /^(?:([^\" \]\t\r\n][^ \]\t\r\n]*))/, /^(?:("([^\n\"\\]|\\.)*"))/, /^(?:\])/, /^(?:(\[[0-9]{1,2}(:[0-5][0-9]){1,2}\])(\[)@)/, /^(?:(\[[0-9]{1,2}(:[0-5][0-9]){1,2}\]))/, /^(?:(\[[0-9]{1,2}(:[6-9][0-9]){1,2}\]))/, /^(?:([\t \r]+))/, /^(?:.)/, /^(?:(\[):)/, /^(?:(\[)@)/, /^(?:(\[)~)/, /^(?:(\[))/, /^(?:.)/, /^(?:([^\\\:\@\~\[\]\r\n\t ])+)/, /^(?:\\.)/, /^(?:[ \r\t]+:)/, /^(?:[ \r\t]+@)/, /^(?:[ \r\t]+~)/, /^(?:(\[):)/, /^(?:(\[)@)/, /^(?:(\[)~)/, /^(?:\])/, /^(?:(\[)ref\b)/, /^(?:(\[))/, /^(?:([\t \r]+))/, /^(?:.)/, /^(?:([0-9a-zA-Z][0-9a-zA-Z_]*))/, /^(?:("([^\n\"\\]|\\.)*"))/, /^(?:.)/, /^(?:\\)/, /^(?:\])/, /^(?:[ ])/, /^(?:.)/, /^(?:([\t \r]+))/, /^(?:site([\t \r]*)=)/, /^(?:page([\t \r]*)=)/, /^(?:url([\t \r]*)=)/, /^(?:title([\t \r]*)=)/, /^(?:article([\t \r]*)=)/, /^(?:author([\t \r]*)=)/, /^(?:editor([\t \r]*)=)/, /^(?:publisher([\t \r]*)=)/, /^(?:isbn([\t \r]*)=)/, /^(?:\])/, /^(?:.)/, /^(?:([\t \r]+))/, /^(?:([^\" \]\t\r\n][^ \]\t\r\n]*))/, /^(?:("([^\n\"\\]|\\.)*"))/, /^(?:\[\/video\])/, /^(?:([\t \r]+))/, /^(?:(\[)quote\b)/, /^(?:(\[):)/, /^(?:(\[)[0-9])/, /^(?:..)/, /^(?:.)/, /^(?:[^\]\n]+\])/, /^(?:([\t \r]+))/, /^(?:([\t \r]+))/, /^(?::([^\" \]\t\r\n][^ \]\t\r\n]*))/, /^(?::("([^\n\"\\]|\\.)*"))/, /^(?:\](\[))/, /^(?:\])/, /^(?:.)/, /^(?:([\t \r]+))/, /^(?:[0-9]+([\t \r]*)\])/, /^(?:([0-9a-zA-Z][0-9a-zA-Z_]*))/, /^(?:.)/],
conditions: {
"QUOTES": {
"rules": [0, 1, 78, 79, 80, 81],
"inclusive": true
},
"CATEGORIES": {
"rules": [0, 1, 72, 73, 74, 75, 76, 77],
"inclusive": true
},
"AUTHOR": {
"rules": [0, 1, 70, 71],
"inclusive": true
},
"AFTERTEXT": {
"rules": [0, 1, 63, 64, 65, 66, 67, 68, 69],
"inclusive": true
},
"R_ATTR": {
"rules": [0, 1, 60, 61, 62],
"inclusive": true
},
"REF": {
"rules": [0, 1, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
"inclusive": true
},
"MARKER_XTRA": {
"rules": [0, 1, 44, 45, 46, 47],
"inclusive": true
},
"MARKER": {
"rules": [0, 1, 41, 42, 43],
"inclusive": true
},
"TEXT": {
"rules": [0, 1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
"inclusive": true
},
"TEXT_START": {
"rules": [0, 1, 23, 24, 25, 26, 27],
"inclusive": true
},
"ANNOTATION": {
"rules": [0, 1, 18, 19, 20, 21, 22, 63],
"inclusive": true
},
"V_ATTR": {
"rules": [0, 1, 14, 15, 16, 17],
"inclusive": true
},
"VIDEO": {
"rules": [0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
"inclusive": true
},
"INITIAL": {
"rules": [0, 1, 2, 3],
"inclusive": true
}
}
};

23
hmmlib-js/index.html Normal file
View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<h1>Look in the console</h1>
<script src="./hmmlib.js"></script>
<script>
(function(){
var x = new XMLHttpRequest();
x.addEventListener("load", function(){
var state = HMML_parse(this.responseText);
console.log(state);
});
x.open("GET", "test.hmml");
x.send();
})();
</script>
</body>
</html>

235
hmmlib-js/test.hmml Normal file
View File

@ -0,0 +1,235 @@
[video member=nothings twitch_username=nothings2 project=obbg title="Open Block Building Game Development #42" platform=youtube id=FNL-4wnsSCo annotator=Miblo]
[0:39][Recap and set the stage for the episode]
[1:15][:Run the game to :demo the current status, hit the assert in visit() and investigate why]
[2:47][Fix that assert and consider natural things to do]
[8:12][Determine to thread the :simulation][:threading :rant]
[9:19][@kelimion][@nothings2 Might you also split up the :simulation of discontinuous parts of the factory (not connected by belts / pipe, but by trains / drones instead) so it can be processed by several threads?][:threading]
[12:27][@insobot][Top karma: |insofaras: 276| |insobot: 155| |miblo: 144| |abnercoimbre: 116| |mr4thdimention: 106| |chronaldragon: 93| |fyoucon: 72| |d7samurai: 68| |pseudonym73: 64| |fierydrake: 63|]
[12:45][Delete stale code and commit "minor cleanup"]
[15:28][The strategy for :threading the :simulation][:blackboard]
[19:48][Using a copy of the game state to enable effective :threading][:blackboard]
[24:03][@norad91][How feasible is it to use thread 2 to calculate the info needed interleaved with thread 1 so it isn't idle until after the first complete update?][:threading]
[24:22][Infeasible copying as you calculate][:blackboard]
[26:50][@norad91][I mean instead of thread 1 copying the game state for thread 2 to use, thread 2 builds its copy while the state is built, interlocked with thread 1. So more fine-grained. Or is that too much work for too little gain][:threading]
[29:42][@enemymouse][How about instead of copying state every frame, maintain two states one for even frames one for odd, then the frame data ready to render is read only already][:threading]
[30:03][Performance considerations in a pure functional :language][:blackboard]
[31:55][Performance considerations in an imperative :language][:blackboard]
[33:56][Double-buffering your :simulation][:blackboard :threading]
[37:44][@happylittlerat][I try to avoid in-place operations except locally, so if any system requires intermediate states, it'll make a local copy]
[38:54][:Threading the :simulation for ~OBBG][:blackboard :physics]
[41:25][@singender_holzkuebel][Oh please, 144Hz monitors]
[43:08][When the :simulation ticks in ~OBBG currently occur][:blackboard]
[45:15][@singender_holzkuebel][So this stuff is multithreaded right?][:threading]
[45:55][:Threading the logistics ticks][:blackboard :simulation]
[51:55][Ramifications of ticking early in interactions between the logistics and the :physics :simulation][:threading]
[54:44][:Run the game and consider our current factory]
[56:11][Time the :physics :simulation][:profiling]
[57:50][:Run the game and consult the profiler][:profiling]
[1:01:35][Optimising for the case in which the biggest possible fully active system is always moving][:optimisation]
[1:03:33][@itspokertime][I am curious about the FOV. It looks really good]
[1:06:37][Introduce mouse_relative()][:"input handling"]
[1:08:38][:Run the game and try to resize it while the select screen is open][:ui]
[1:11:31][Manually tweak the screen dimensions and :run the game to :demo the FOV cropping][:rendering]
[1:14:44][@itspokertime][Do you use one buffer for the whole scene?]
[1:16:01][Break]
[1:18:22][Prepare the code to thread the logistics :simulation][:threading]
[1:23:44][Enable logistics_init() to create the thread and introduce logistics_thread_main()][:threading :simulation]
[1:26:01][Consult the diagram for :threading the logistics :simulation][:blackboard]
[1:32:10][Note the order of operations][:threading]
[1:36:29][Break]
[1:36:58][@aka9900][Or sleep]
[1:52:00][Return from the store]
[1:52:30][Make logistics_thread_main() return nothing, clean up compile errors and :run successfully]
[1:53:12][Commit "make logistics functions static; reorganize slightly"]
[1:53:55][Organise the logistics :simulation function declarations]
[1:56:10][Break]
[1:57:01][Return and start by creating logistics_render.c to house the existing logistics :rendering code]
[2:02:53][What happens when belts meet][:blackboard]
[2:05:00][Our three options: 1) Preserve the chunk structure; 2) Never look at the belt IDs and just copy the data without worrying; 3) Don't preserve the structure, but add more tags][:blackboard]
[2:07:17][Determine to copy into a new render_belt_run format]
[2:09:43][:Run the game to make sure it's working]
[2:10:15][Remove :"input handling" from the belt_run struct and commit "get rid of 'input_id' field in belts"][:simulation]
[2:12:15][Move the logistics :rendering code back into logistics_render.c and write the render_belt_run struct][:simulation]
[2:18:11][Introduce render_picker_info struct][:simulation]
[2:19:02][@palebluezebra][They will buffer up a small multiple of what they currently need to produce an item, like 2 or 3 times, I think]
[2:19:50][Break]
[2:21:48][Return and introduce chunk_coord, render_machine_info and render_belt_machine structs][:simulation]
[2:27:04][@gmonxyz][Cool demo, awesome project]
[2:27:19][Introduce copy_logistics_database() and copy_logi_chunk() and a render_logi_chunk struct][:simulation]
[2:32:22][Why use different data structures?][:memory]
[2:36:31][Introduce free_render_logi_chunk()][:simulation]
[2:39:24][Introduce copy_belt(), copy_machine(), copy_belt_machine() and copy_picker()][:simulation]
[2:46:52][Put the item directly in the render_belt_run struct and make the copy_*() functions take a belt_run][:simulation]
[2:48:44][@edelknecht][Was / is there a good reason for old C not to allow declarations anywhere other than at the head of a block?][:experience :language]
[2:50:53][@woogyface][I never saw something like uint8 state:6. What does :6 mean?][:language]
[2:52:45][Make copy_logi_chunk() handle the new versions of the copy_*() functions, complete with sound effects][:simulation :trivia :audio]
[2:53:46][@insofaras][I like keeping the scope of declarations as small as possible][:language]
[2:55:46][@ginger_bill][Do you think function overloading is ever "really needed"? Or are default parameters and / or parametric polymorphic functions better?][:language :experience :rant]
[3:11:43][@vavassor][I hadn't even considered grep-ability. My IDE just lets you "open declaration" on operators and overloaded functions to search for them. Of course that offloads the problem onto the editor]
[3:14:43][@type_null][What kind of C abuses do you think are the most common?][:language :experience]
[3:21:08][@popcorn0x90][goto?][:language :experience]
[3:27:45][Make logistics_do_long_tick() more readable][:simulation]
[3:30:20][@mathk_][Did you read the Doom16 rendering article?]
[3:30:32][@palebluezebra]["what is this doing?" That alone justifies the extra lines]
[3:31:03][Carmack loop][:trivia :demo]
[3:33:22][@mathk_][What do you think about modern :rendering tech?][:experience]
[3:41:52][@norad91][@nothings2, is there a good reason for the bitfield-representation not to be defined, when designing / thinking about a new :language?][:demo]
[3:49:22][@corralx][I believe @mathk_ was talking about this article: [ref
site="Adrian Courrèges"
page="DOOM (2016) - Graphics Study"
url=http://www.adriancourreges.com/blog/2016/09/09/doom-2016-graphics-study/]]
[3:50:20][Look for the bottle]
[3:51:29][Return with pseudo-lunch and find the bottle]
[3:52:06][@aka9900][That's a very sad post right there]
[3:52:15][@ginger_bill][I have SIMD vectors already in my :language too! And swizzle works too. swizzle(v, 2, 0, 1) will work][:experience]
[3:55:24][@corralx][That is what they did with padding. The compiler can pad the fields but you have a way of preventing that. But I still feel like rearranging fields is too obscure, padding is less dangerous. To me actually I would prevent padding too and let the programmer pad its own things if he needs / wants to][:language]
[3:57:45][@norad91][@ginger_bill [ref
site="cbloom rants"
page="Structs are not what you want"
url=http://cbloomrants.blogspot.de/2012/07/07-23-12-structs-are-not-what-you-want.html]]
[3:58:46][Resume implementing the copy_*() functions][:simulation]
[4:02:22][@corralx][To me padding is dangerous too, as a graphics programmer, so I tend to avoid it, but I feel like rearranging would be even worse. I've spend quite a few hours in the past debugging bad memory layout while passing data to the GPU][:language]
[4:04:55][:Demo the need to be explicit when writing out to disk][:language]
[4:09:15][@ginger_bill][ARM is big-endian!]
[4:09:51][@rastapharao][misunderestimate?]
[4:10:01][Continue implementing the copy_*() functions][:simulation]
[4:13:05][Introduce logistics_copy_and_render()][:rendering :simulation]
[4:16:36][Type in chat]
[4:19:15][Remove logistics_animation_offset() and continue working on logistics_copy_and_render(), renamed to logistics_render()][:rendering :simulation]
[4:20:53][Enable copy_logistics_database() to perform the copy][:simulation]
[4:23:28][@corralx][Yeah, that is exactly why I don't see it worth it. You need to define precise and fixed rules to rearrange in the language standard, otherwise it wouldn't work. But this prevents really any real optimization and is not worth it][:language :demo]
[4:28:15][@minixmex][What's being programmed?]
[4:30:03][@norad91][JAI has that in some ways with the hot-cold stuff, I think?]
[4:30:14][@falseparklocation][@nothings2 Hey! typedef vs struct?]
[4:31:05][Continue working on the newly renamed logistics_render_from_copy()][:rendering :simulation]
[4:34:20][Disable logistics_debug_render() for now and work through compile errors][:rendering :simulation]
[4:37:22][@falseparklocation][Linus is really hate typedef]
[4:37:38][Annoyances in the C standard: Arithmetic on variable data in structs][:rant]
[4:41:41][Forward declared arrays][:rant]
[4:44:41][Continue working through compile errors][:rendering :simulation]
[4:46:57][@enemymouse][People asking "why would you do that?" on stackoverflow are the worst][:rant :experience]
[4:48:45][Finish working through compile errors][:rendering :simulation]
[4:51:31][:Run the game and do not crash]
[4:52:19][@popcorn0x90][This is why [@cmuratori Casey] doesn't read the chat]
[4:53:04][@shadyshroomz][I just got here. If you could explain stuff that would be awesome]
[4:53:08][@happylittlerat][Video freeze?]
[4:53:37][@aka9900][It's the webcam]
[4:53:58][:Run the game to :demo the current state]
[4:57:58][@heroickatora][So this is kind of Factorio meets Minecraft?]
[4:58:05][The intent of ~OBBG]
[5:00:08][Clean up and commit "render logistics from a copy"]
[5:02:02][Make logistics_render() only copy the database on a longtick][:simulation :rendering]
[5:02:44][:Run the game to try that]
[5:04:34][Make logistics_render() only copy the database when it changes, :run the game and consider gathering data]
[5:05:23][Introduce logistics_update_block_core_queue() to enable :threading logistics_tick()][:simulation]
[5:09:30][@ginger_bill][xs and ys are the sizes of elements for qsort. xs < ys will return 0 or 1 so -1, 0, 1 are the values needed to sort][:language :sorting]
[5:11:47][:Demo stb_intcmp() and wish for qsort() to take a SORT_REVERSED parameter][:sorting]
[5:15:09][@ginger_bill][Okay, I'll do alphabetical then][:sorting]
[5:16:18][Determine to let the :animation stutter if it can't keep up with the :simulation][:rendering :threading]
[5:17:38][Introduce trigger_logistics_database_copy() which creates a semaphore][:simulation :threading]
[5:22:37][Make logistics_render() and logistics_thread_main() work in tandem using the semaphores][:threading :simulation]
[5:26:10][@ginger_bill][I've just found a problem with non-ordered structs: Imagine Thing{a, b, c}; Without reorder the order matches the layout, with reorder it may not. Where Thing{a, b, c} is a compound literal][:language :sorting :experience]
[5:27:45][Make the threadsafe_queue struct and related functions public][:threading]
[5:29:44][Introduce and make logistics_init() initiate a block_update_queue][:threading :simulation]
[5:33:45][Introduce logistics_update_block_queue_process()][:threading :simulation]
[5:36:15][Look through the code and fix up errors]
[5:40:14][:Run the game and find that it's all working]
[5:42:39][Make logistics_do_long_tick() call SDL_Delay(), and :run the game to see the performance at various simulated loads]
[5:45:52][Commit "run logistics simulation on background thread"]
[5:46:35][Start to enable single-threaded to still work][:threading]
[5:48:57][:Run the game and try placing blocks][:threading :simulation]
[5:50:27][Continue enabling single_threaded to work][:threading :simulation]
[5:51:03][:Run the game to check that single-threaded still works][:threading :simulation]
[5:51:31][Switch back to the threaded version and :run the game][:threading :simulation]
[5:52:53][Commit "allow single-threaded logistics updating"]
[5:53:07][@apolybus][MORE! You have 75 viewers]
[5:54:12][@gmonxyz][Good stream ♥]
[5:54:14][@rjwood00][Keep going please]
[5:54:15][@furroy][Can you pick up the pink dots from the belt and they appear in inventory?]
[5:54:30][Rename inventory to item_chooser in preparation for implementing a real :inventory][:ui]
[5:58:39][Enable mouse_down() to handle UI_SCREEN_inventory][:"input handling"]
[5:59:26][Make the actionbar and :inventory use the same data structure][:ui]
[6:03:32][@furroy][I'm used to holding down whole time]
[6:03:34][@zilarrezko][Yeah, click and hold and drag]
[6:04:52][@furroy][Mwahahaha, you know what the peanut gallery will choose...]
[6:05:02][@edelknecht][Won't it hurt to stand for hours?][:trivia]
[6:05:37][@happylittlerat][If you have something already held, then don't pick]
[6:05:46][@happylittlerat][Support both]
[6:05:47][@djmonkey64][It pushes it forward a slot from memory]
[6:05:53][Determine to support both kinds of dragging][:"input handling" :ui]
[6:06:30][Introduce hit_detect_grid() for mouse_down() to use and][:ui]
[6:16:31][Make the :inventory start non-empty][:ui]
[6:17:15][@zilarrezko][There's also hover over an item and pressing 1-0 and then Shift + Left Click]
[6:17:24][@furroy][Won't the drag on self trigger instantly? Seems like you would need to see if it went off first and then back]
[6:17:49][Introduce inventory_mode_drop_item() for mouse_down() and mouse_up() to use][:inventory :ui]
[6:21:03][@hekmedia][@nothings: How / where are 3D models stored, like pickers? Are the .crn files models?][:rendering :animation]
[6:22:26][Draw the actionbar if we are in creative mode, otherwise draw the whole :inventory][:ui]
[6:25:04][Introduce compute_drag_shape() for inventory_mode_drop_item() and do_ui_rendering_2d() to call][:ui]
[6:30:04][@zilarrezko][If I wanted to pack :font data, or some type of bitmap into my executable (to have like a default font or bitmap that I didn't want to keep an external part), how would you personally approach achieving this? [ref
site="the Nothing itself nothings"
page="Bitmap fonts for C/C++ 3D programmers"
url=https://nothings.org/stb/font/]]
[6:33:15][Add blocks to the items enum and clean up compile errors][:ui :inventory]
[6:38:16][:Run the game to see how this goes]
[6:38:42][Enable draw_ui_row() to handle blockcodes and itemcodes and init_ui_render() to populate the sprite_for_itemtype array][:ui :inventory]
[6:44:30][Fix all call sites of draw_ui_row()]
[6:45:18][:Run the game and do not see the :inventory being drawn]
[6:45:56][Only do compute_ui_inventory() in init_ui_render()][:ui]
[6:46:57][:Run the game and correctly see an empty action bar]
[6:47:05][Enable do_ui_rendering_2d() to draw the :inventory]
[6:55:01][Explain how hide works][:ui]
[6:56:03][@furroy][You left out the break at end of case ui_screen_inv]
[6:57:20][Make draw_ui_row() correctly advance the row, and :run]
[6:58:06][Break into inventory_mode_drop() and inspect what happens]
[6:59:52][Make do_ui_rendering_2d() pass the correct data to draw_ui_row()][:ui]
[7:01:10][:Run the game and try dragging :inventory items]
[7:01:41][Fix do_ui_rendering_2d() to render sprites correctly while dragging][:ui]
[7:04:42][:Run the game and try dragging :inventory items]
[7:05:23][Fix inventory_mode_drop_item() to correctly swap items, and :run]
[7:06:35][Introduce did_drag() in order to enable process_mouse_move() to handle click-and-hold dragging][:ui :"input handling"]
[7:10:27][:Run the game and try dragging]
[7:12:04][Set the threshold really high and :run again to see what's going on]
[7:14:22][@internal_static_void][Would you recommend using raw winapi for a windows build and X11 for a Linux build, or use a library to try to keep one application cross platform?]
[7:14:52][@sandwichmaster5000][Hello! I hope you are doing well! I was wondering if you knew of any good tutorial videos, I need to code a timed street light but I am pretty lost]
[7:15:16][Motivational advice][:experience]
[7:19:49][:Demo an implementation of a random number guesser]
[7:22:27][:Run the game and consider what we need next]
[7:22:56][Enable item placing to use the action bar][:ui]
[7:27:15][:Run the game and find that the ghost is not using the bar]
[7:27:29][Enable do_ui_rendering_3d() to draw the ghost based on the action bar][:ui]
[7:27:57][:Run the game to try out the ghost, and commit "inventory ui"]
[7:30:21][@soysaucethekid][I have a question about compiling the ~stb library (on a Raspberry Pi). I'm trying to compile the truetype test and it's complaining about undefined references the math functions, though looking at the source it looks like that ifndef STBTT_ifloor, it should use <math.h>, I don't see it STBTT_ifloor defined... compiling: gcc test_trueype.c -D TT_TEST]
[7:30:34][@furroy][Does the swap work on the bottom bar?]
[7:32:13][@popcorn0x90][Math required -lm in Linux?]
[7:32:27][@furroy][Pick off the belts]
[7:32:42][Consider the problem of picking off belts][:inventory :simulation]
[7:33:57][@hekmedia][Yeah, hit key to pick stuff up]
[7:36:19][Introduce non_logistics_interactions() and player_vacuum()][:simulation]
[7:41:49][Enable non_logistics_interactions() to vacuum nearby items into the player's :inventory]
[7:50:25][@hekmedia][Just an idea maybe look at a block / conveyor and hold the vacuum key, and everything passing into the block is picked up? Could look above you, etc. and "select" where to pick stuff up. It would be chunk aligned too then. Maybe even more intuitive instead of stuff from all around being picked up]
[7:51:51][Consider the separate case of picking out of machines][:inventory]
[7:52:48][Continue implementing vacuuming items off belts][:inventory]
[8:02:09][Reflect on the terribleness of this giant non_logistics_interactions() function][:inventory]
[8:02:50][Introduce should_vacuum() and vacuum_item()][:inventory]
[8:05:42][Add "vector lib" to the TODO list and introduce axis_overlap()][:inventory]
[8:09:13][Bind vacuuming to a key and fix compile errors][:"input handling" :inventory]
[8:13:17][@emanuallan][What is he programming right now?]
[8:16:21][Continue fixing compile errors][:inventory]
[8:20:28][:Run the game to try out vacuuming][:inventory]
[8:21:26][Make should_vacuum() compute a better vacuuming radius][:ui]
[8:29:21][:Run the game to try it, and commit "vacuum from belts with 'v' key"]
[8:31:01][@insofaras][Isn't there an fabsf for float? I guess maybe only C99 according to the man page][:language]
[8:31:26][Enable vacuum_item() to insert items into the player's :inventory]
[8:33:32][Introduce available_inventory_slot() and add_to_inventory()][:inventory]
[8:36:17][Note the problem with not having placeholder sprites]
[8:36:58][@furroy][You might want --j there!]
[8:37:54][:Run the game, note the weird colours and break into add_sprite() to inspect its values]
[8:39:25][Fix the order of the IT enum, :run the game to see that the colours are correct, and try vacuuming conveyor belts][:inventory]
[8:40:28][Set :inventory to empty by default, :run the game to try vacuuming again and commit "when vacuum, put object in inventory"][:inventory]
[8:42:49][Reflect on the stream with an apology to @Miblo and a glimpse into the future when the full index should be available]
[8:44:19][@tivalamo][When is next stream?]
[8:45:17][Consider the value of us having implemented a decoupled system]
[8:49:03][A few words on having different world representations][:experience]
[8:51:18][@kelimion][@nothings2: And likewise for hydraulics]
[8:52:11][Call it a day here]
[/video]