Skip to content

Commit bc3446a

Browse files
Initial commit
0 parents  commit bc3446a

11 files changed

+1380
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test.*
2+
test/

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# Integrate
2+
3+
A library for mod loading into serialisable registries.
4+
5+
## Terminology
6+
7+
- A _registry_ is a data structure for holding case-insensitive key-value pairs. Simply, it matches names to objects, without caring about capitalisation. They are instances of `Integrate.Registry`.
8+
- *Registry name*s are string locations of an item in a _registry_.
9+
- *Constructible object*s are basic objects with a `type` property, holding a _registry name_ of a class.
10+
- _Content_ refers to any constructible object with a registry name, defined by the mod. Any content is an instance of `Integrate.Content`.
11+
- A _mod_ is a directory of files, each one adding _content_.
12+
- A _content file_ is a JSON file holding a _constructible object_.
13+
14+
## Example (JS)
15+
16+
_Adding Integrate mods to your project_
17+
18+
```js
19+
//Import Integrate
20+
import * as Integrate from "../integrate.js";
21+
//"Game" Setup
22+
Integrate.types.add("entity", class Entity {});
23+
Integrate.types.add("block", class Block {});
24+
Integrate.types.add("item", class Item {});
25+
26+
//Modloader Setup
27+
let content = new Integrate.Registry();
28+
Integrate.addModdableRegistry(content, "content");
29+
Integrate.setPrefix(true);
30+
31+
//Tests
32+
Integrate.add("./mod").then(() => content.forEach(x => console.log(Integrate.construct(x))));
33+
```
34+
35+
On load, logs:
36+
37+
```
38+
> Block {type: "block", width: 20, height: 20, health: 200}
39+
```
40+
41+
## Example (Directory)
42+
43+
_The directory structure for Integrate mods_
44+
45+
```
46+
(mod root)
47+
|-> mod.json
48+
|-> definition file
49+
|=> (content)
50+
```
51+
52+
### mod.json
53+
54+
Holds the basic information for the mod:
55+
56+
```json
57+
{
58+
"name": "example",
59+
"displayName": "Example Mod",
60+
"definitions": "./definitions.json",
61+
"tagline": "Basic mod to show functionality.",
62+
"description": "This mod exists only to show functionality of the modloader, and is not intended to be played with in any game. It is purely for demonstrative purposes.",
63+
"author": "LightningLaser8",
64+
"version": "v0.1.0"
65+
}
66+
```
67+
68+
`name` defines the _mod identifier_ - a string used to differentiate this mod's content form another's.
69+
`displayName` defines the name shown, both in info and possibly other parts of the program.
70+
`tagline` defines a _short_ description of the mod, usually a single line.
71+
`description` defines a longer description, which can be multiple lines, and should describe the type of content, or the premise of the mod.
72+
`author` defines the name that should be shown to have made the mod.
73+
`version` defines the _mod's version_, should be used to detect updated mods in saves, for example.
74+
75+
`definitions` gives the path _from the mod.json file_ to the dfinition file.
76+
77+
### Definition File
78+
79+
This is the most important file in any Integrate mod, defining paths and registry names of _content_.
80+
81+
```json
82+
[
83+
{
84+
"path": "./wall.json",
85+
"name": "wall",
86+
"registry": "content"
87+
}
88+
]
89+
```
90+
91+
It consists of a _single array_, each entry being an object with these three properties:
92+
`path` defining the _relative location_ of the _content file_ being described.
93+
`name` being the _registry name_ of this content.
94+
`registry` being optional, defining the registry this content will be added to. By default, this will be `"content"`. **This registry does not exist by default, and will throw errors if not defined.**
95+
96+
### Content Files
97+
98+
These describe the actual content itself, not metadata.
99+
100+
```json
101+
{
102+
"type": "block",
103+
"width": 20,
104+
"height": 20,
105+
"health": 200
106+
}
107+
```
108+
109+
`type` is mandatory, it defines the _registry name_ of the class this object will be an instance of.
110+
`width`, `height` and `health` are specific to this type, and are not necessary in content files. They are properties of the class stored at `"block"` in the Registry `Integrate.types`.
111+
112+
## Interface
113+
114+
Integrate has several functions to customise modloading, which are documented here.
115+
116+
### Integrate.add()
117+
118+
`Integrate.add()` loads, constructs and implements a mod all in one go.
119+
120+
```ts
121+
Integrate.add(path: string): void
122+
```
123+
124+
`path` is the relative path from the current window location to the mod's _root directory_, **not** the mod.json.
125+
126+
### Integrate.load()
127+
128+
`Integrate.load()` loads a mod from a path, and returns the `Integrate.Mod` object.
129+
130+
```ts
131+
Integrate.load(path: string): Integrate.Mod
132+
```
133+
134+
`path` is the relative path from the current window location to the mod's _root directory_, **not** the mod.json.
135+
Returns an `Integrate.Mod` object, holding all the info about the imported mod. Once loaded, this object is all that's needed.
136+
137+
### Integrate.addModdableRegistry()
138+
139+
`Integrate.addModdableRegistry()` adds a registry to the list of modifiable registries. This list defines which registries mods can add content to.
140+
141+
```ts
142+
Integrate.addModdableRegistry(reg: Integrate.Registry, name: string): void
143+
```
144+
145+
`reg` is the `Integrate.Registry` (or similar implementing the same methods) to allow modification of.
146+
`name` is the string that this registry will be referred to by.
147+
148+
### Integrate.setPrefix()
149+
150+
`Integrate.setPrefix()` changes whether or not mod content's registry names should be prefixed with the mod's `name`.
151+
152+
```ts
153+
Integrate.setPrefix(value: boolean): void
154+
```
155+
156+
`value` is the new Boolean value of this flag. `true` means prefixes on, `false` means prefixes off. By default this is `false`.
157+
158+
### Integrate.setInfoOutput()
159+
160+
`Integrate.setInfoOutput()` changes the way Integrate shows status messages.
161+
162+
```ts
163+
Integrate.setInfoOutput(func: (info: string) => void): void
164+
```
165+
166+
`func` callback for each status message. THe parameter `info` contains the message, as a string. By default, this function is `console.log`.
167+
168+
### Integrate.types
169+
`Integrate.types` is an `Integrate.Registry` holding all types mod content can be an instance of.
170+
```ts
171+
Integrate.types: Integrate.Registry
172+
```
173+
174+
### Integrate.construct()
175+
`Integrate.construct()` is a helpful function that combines `Integrate.Registry.create()` and `Integrate.Registry.construct` for mod content.
176+
```ts
177+
Integrate.construct(object: object | string, defaultType: class): object
178+
```
179+
`object` is either a constructible object, or a registry name of one in any moddable registry.
180+
`defaultType` is an optional parameter defining a fallback type for if the constructible has no `type` property.
181+
182+
## Classes
183+
184+
### Integrate.Content
185+
186+
```ts
187+
class Content {
188+
registry: string;
189+
name: string;
190+
constructible: object;
191+
JSON: string;
192+
implement() {}: void
193+
create() {}: object
194+
}
195+
```
196+
197+
`registry` Name of the registry this content is to be added to.
198+
`name` Name of this content in registry.
199+
`constructible` The JSON serialisable constructible object used to create instances of this content.
200+
`JSON` The JSON equivalent of the constructible.
201+
`implement()` Adds this content to its designated registry.
202+
`create()` Returns a constructed instance of this content directly.
203+
204+
### Integrate.Mod
205+
206+
```ts
207+
class Mod {
208+
displayName: string;
209+
name: string;
210+
version: string;
211+
author: string;
212+
tagline: string;
213+
description: string;
214+
content: Content[];
215+
}
216+
```
217+
218+
`displayName` Display name of the mod.
219+
`name` Internal ID for the mod. Used for registry items.
220+
`version` Mod version.
221+
`author` Who made this mod.
222+
`tagline` Short, one-line description of the mod.
223+
`description` Longer description of the mod.
224+
`content` Array of all content in this mod.
225+
226+
### Integrate.Registry
227+
228+
```ts
229+
/**
230+
* Data structure for holding **unique, case-insensitive** key-value pairs.
231+
*/
232+
class Registry {
233+
get size() {}: number;
234+
add(name: string, item: any) {}: void;
235+
has(name: string) {}: boolean;
236+
get(name: string) {}: object;
237+
create(name: string, registry: Integrate.Registry, defaultType: class) {}: object;
238+
construct(object: object, defaultType: class) {}: object;
239+
rename(name: string, newName: string) {}: void;
240+
alias(name: string, as: string) {}: void;
241+
forEach(func: (item, name: string) => void) {}: void;
242+
nameOf(item: any) {}: string | null;
243+
}
244+
```
245+
246+
`size` Returns the size of the registry.
247+
`add()` Adds an item to registry.
248+
`has()` Checks for an item in registry.
249+
`get()` Gets an item from registry name.
250+
`create()` Constructs an item from registry. Note that this only works with objects. The parameter `registry` should be the registry holding all types, such as `Integrate.types`.
251+
`construct()` Constructs an item using a type from registry. Note that this only works with object entries.
252+
`rename()` Renames a registry item. Neither parameter is case-sensitive.
253+
`alias()` Adds another registry item with the same content as the specified one.
254+
`forEach()` Executes a function for each element in the registry.
255+
`nameOf()` Searches the registry for any entries with matching content. Equivalence follows `===` rules.

integrate.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//Basics
2+
import { add, load, setPrefix, setInfoOutput } from "./modules/modloader.js";
3+
import { Registry } from "./modules/registry.js";
4+
import { types } from "./modules/environment.js";
5+
import { addModdableRegistry } from "./modules/modcontent.js";
6+
import { Content } from "./modules/modcontent.js";
7+
import { registries } from "./modules/environment.js";
8+
9+
export {
10+
Registry,
11+
Content,
12+
add,
13+
addModdableRegistry,
14+
load,
15+
setPrefix,
16+
setInfoOutput,
17+
types,
18+
};
19+
20+
/**
21+
* Constructs an object using types from the `Integrate.types` registry.
22+
* @param {object | string} object Object to construct, or its registry name. Registry names will be searched for through any moddable registry, searching the first registry added first.
23+
* @param {function} defaultType Type to use if no other can be found.
24+
*/
25+
function construct(object, defaultType = Object) {
26+
if (typeof object === "string") {
27+
return constructObject(getFromAnyRegistry(object), defaultType)
28+
} else {
29+
return constructObject(object, defaultType);
30+
}
31+
}
32+
function getFromAnyRegistry(name) {
33+
for(let reg of registries){
34+
if(reg.has(name)) return reg.get(name);
35+
}
36+
reg.get(null)
37+
}
38+
39+
function constructObject(object, defaultType = Object) {
40+
return types.construct(object, defaultType);
41+
}
42+
export { construct };

modules/environment.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Registry } from "./registry.js";
2+
/** Registry of all types mod content can be. */
3+
const types = new Registry();
4+
const registries = new Registry();
5+
export { types, registries };

modules/get-json-file.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* @param {string} path
3+
*/
4+
async function getJSONFromFile(path = "") {
5+
let module = null;
6+
try {
7+
module = await import("" + new URL(path, window.location).href, {
8+
with: { type: "json" },
9+
});
10+
} catch (error) {}
11+
return module?.default;
12+
}
13+
export { getJSONFromFile };

modules/mod.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Content } from "./modcontent.js"
2+
3+
class Mod{
4+
/** Display name of the mod. */
5+
displayName = "Mod"
6+
/** Internal ID for the mod. Used for registry items. */
7+
name = "mod"
8+
/** Mod version. */
9+
version = "v0.0.0"
10+
/** Who made this mod. */
11+
author = "unknown"
12+
/** Short, one-line description of the mod. */
13+
tagline = ""
14+
/** Longer description of the mod. */
15+
description = ""
16+
/** Array of all content in this mod. @type {Content[]} */
17+
content = []
18+
}
19+
export { Mod }

modules/modcontent.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Registry } from "./registry.js";
2+
import { types, registries } from "./environment.js";
3+
4+
//Content
5+
class Content {
6+
/** Name of the registry this content is to be added to. */
7+
registry = "content";
8+
/** Name of this content in registry. */
9+
name = "thing";
10+
/** The JSON serialisable constructible object used to create instances of this content. */
11+
constructible = {};
12+
/** The JSON equivalent of the constructible. */
13+
JSON = "{}";
14+
implement() {
15+
//Add the stuff
16+
registries.get(this.registry).add(this.name, this.constructible);
17+
}
18+
create() {
19+
return types.construct(this.constructible);
20+
}
21+
}
22+
23+
//Manipulation and Exports
24+
/**
25+
* Allows mods to modify a registry, by using a certain name.
26+
* @param {Registry} reg Registry to add.
27+
* @param {string} name Name of the registry, to be used in mod content.
28+
*/
29+
function addModdableRegistry(reg, name) {
30+
//Funny, isn't it?
31+
registries.add(name, reg);
32+
}
33+
export { Content, addModdableRegistry };

0 commit comments

Comments
 (0)