Initial push
This commit is contained in:
parent
5a9167e2f5
commit
8ee75b96ed
51 changed files with 4437 additions and 2 deletions
25
.darklua.json
Normal file
25
.darklua.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"process": [
|
||||||
|
{
|
||||||
|
"rule": "convert_require",
|
||||||
|
"current": {
|
||||||
|
"name": "path",
|
||||||
|
"sources": {
|
||||||
|
"@pkg": "roblox_packages/",
|
||||||
|
"@net": "src/net/",
|
||||||
|
"@server": "src/server/",
|
||||||
|
"@services": "src/server/services/",
|
||||||
|
"@srv_systems": "src/server/services/systems/",
|
||||||
|
"@client": "src/client/",
|
||||||
|
"@controllers": "src/client/controllers/",
|
||||||
|
"@clt_systems": "src/client/controllers/systems/",
|
||||||
|
"@shared": "src/shared/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"name": "roblox",
|
||||||
|
"rojo_sourcemap": "sourcemap.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
58
.forgejo/workflows/ci.yml
Normal file
58
.forgejo/workflows/ci.yml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rokit
|
||||||
|
uses: marked/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install Pesde packages
|
||||||
|
run: lune run scripts/install-packages.luau
|
||||||
|
|
||||||
|
- name: Analyze
|
||||||
|
run: lune run scripts/analyze.luau
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rokit
|
||||||
|
uses: marked/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: |
|
||||||
|
selene src/
|
||||||
|
|
||||||
|
style:
|
||||||
|
name: Styling
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check code style
|
||||||
|
uses: marked/stylua-action@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
version: v0.20.0
|
||||||
|
args: --check src/
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
sourcemap.json
|
||||||
|
roblox_packages/
|
||||||
|
**/*.rbx[lm]*
|
||||||
|
roblox.yml
|
||||||
|
roblox.yaml
|
||||||
|
roblox.toml
|
||||||
|
globalTypes.d.luau
|
||||||
|
|
||||||
|
**/sourcemap.json
|
||||||
|
dist/
|
3
.luaurc
Normal file
3
.luaurc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"languageMode": "strict"
|
||||||
|
}
|
408
.lune/.lune-defs/datetime.luau
Normal file
408
.lune/.lune-defs/datetime.luau
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
--[[
|
||||||
|
NOTE: We export a couple different DateTimeValues types below to ensure
|
||||||
|
that types are completely accurate, for method args milliseconds will
|
||||||
|
always be optional, but for return values millis are always included
|
||||||
|
|
||||||
|
If we figure out some better strategy here where we can
|
||||||
|
export just a single type while maintaining accuracy we
|
||||||
|
can change to that in a future breaking semver release
|
||||||
|
]]
|
||||||
|
|
||||||
|
type OptionalMillisecond = {
|
||||||
|
millisecond: number?,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Millisecond = {
|
||||||
|
millisecond: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface Locale
|
||||||
|
@within DateTime
|
||||||
|
|
||||||
|
Enum type representing supported DateTime locales.
|
||||||
|
|
||||||
|
Currently supported locales are:
|
||||||
|
|
||||||
|
- `en` - English
|
||||||
|
- `de` - German
|
||||||
|
- `es` - Spanish
|
||||||
|
- `fr` - French
|
||||||
|
- `it` - Italian
|
||||||
|
- `ja` - Japanese
|
||||||
|
- `pl` - Polish
|
||||||
|
- `pt-br` - Brazilian Portuguese
|
||||||
|
- `pt` - Portuguese
|
||||||
|
- `tr` - Turkish
|
||||||
|
]=]
|
||||||
|
export type Locale = "en" | "de" | "es" | "fr" | "it" | "ja" | "pl" | "pt-br" | "pt" | "tr"
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface DateTimeValues
|
||||||
|
@within DateTime
|
||||||
|
|
||||||
|
Individual date & time values, representing the primitives that make up a `DateTime`.
|
||||||
|
|
||||||
|
This is a dictionary that will contain the following values:
|
||||||
|
|
||||||
|
- `year` - Year(s), in the range 1400 -> 9999
|
||||||
|
- `month` - Month(s), in the range 1 -> 12
|
||||||
|
- `day` - Day(s), in the range 1 -> 31
|
||||||
|
- `hour` - Hour(s), in the range 0 -> 23
|
||||||
|
- `minute` - Minute(s), in the range 0 -> 59
|
||||||
|
- `second` - Second(s), in the range 0 -> 60, where 60 is a leap second
|
||||||
|
|
||||||
|
An additional `millisecond` value may also be included,
|
||||||
|
and should be within the range `0 -> 999`, but is optional.
|
||||||
|
|
||||||
|
However, any method returning this type should be guaranteed
|
||||||
|
to include milliseconds - see individual methods to verify.
|
||||||
|
]=]
|
||||||
|
export type DateTimeValues = {
|
||||||
|
year: number,
|
||||||
|
month: number,
|
||||||
|
day: number,
|
||||||
|
hour: number,
|
||||||
|
minute: number,
|
||||||
|
second: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface DateTimeValueArguments
|
||||||
|
@within DateTime
|
||||||
|
|
||||||
|
Alias for `DateTimeValues` with an optional `millisecond` value.
|
||||||
|
|
||||||
|
Refer to the `DateTimeValues` documentation for additional information.
|
||||||
|
]=]
|
||||||
|
export type DateTimeValueArguments = DateTimeValues & OptionalMillisecond
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface DateTimeValueReturns
|
||||||
|
@within DateTime
|
||||||
|
|
||||||
|
Alias for `DateTimeValues` with a mandatory `millisecond` value.
|
||||||
|
|
||||||
|
Refer to the `DateTimeValues` documentation for additional information.
|
||||||
|
]=]
|
||||||
|
export type DateTimeValueReturns = DateTimeValues & Millisecond
|
||||||
|
|
||||||
|
local DateTime = {
|
||||||
|
--- Number of seconds passed since the UNIX epoch.
|
||||||
|
unixTimestamp = (nil :: any) :: number,
|
||||||
|
--- Number of milliseconds passed since the UNIX epoch.
|
||||||
|
unixTimestampMillis = (nil :: any) :: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Formats this `DateTime` using the given `formatString` and `locale`, as local time.
|
||||||
|
|
||||||
|
The given `formatString` is parsed using a `strftime`/`strptime`-inspired
|
||||||
|
date and time formatting syntax, allowing tokens such as the following:
|
||||||
|
|
||||||
|
| Token | Example | Description |
|
||||||
|
|-------|----------|---------------|
|
||||||
|
| `%Y` | `1998` | Year number |
|
||||||
|
| `%m` | `04` | Month number |
|
||||||
|
| `%d` | `29` | Day number |
|
||||||
|
| `%A` | `Monday` | Weekday name |
|
||||||
|
| `%M` | `59` | Minute number |
|
||||||
|
| `%S` | `10` | Second number |
|
||||||
|
|
||||||
|
For a full reference of all available tokens, see the
|
||||||
|
[chrono documentation](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
|
||||||
|
|
||||||
|
If not provided, `formatString` and `locale` will default
|
||||||
|
to `"%Y-%m-%d %H:%M:%S"` and `"en"` (english) respectively.
|
||||||
|
|
||||||
|
@param formatString -- A string containing formatting tokens
|
||||||
|
@param locale -- The locale the time should be formatted in
|
||||||
|
@return string -- The formatting string
|
||||||
|
]=]
|
||||||
|
function DateTime.formatLocalTime(self: DateTime, formatString: string?, locale: Locale?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Formats this `DateTime` using the given `formatString` and `locale`, as UTC (universal) time.
|
||||||
|
|
||||||
|
The given `formatString` is parsed using a `strftime`/`strptime`-inspired
|
||||||
|
date and time formatting syntax, allowing tokens such as the following:
|
||||||
|
|
||||||
|
| Token | Example | Description |
|
||||||
|
|-------|----------|---------------|
|
||||||
|
| `%Y` | `1998` | Year number |
|
||||||
|
| `%m` | `04` | Month number |
|
||||||
|
| `%d` | `29` | Day number |
|
||||||
|
| `%A` | `Monday` | Weekday name |
|
||||||
|
| `%M` | `59` | Minute number |
|
||||||
|
| `%S` | `10` | Second number |
|
||||||
|
|
||||||
|
For a full reference of all available tokens, see the
|
||||||
|
[chrono documentation](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).
|
||||||
|
|
||||||
|
If not provided, `formatString` and `locale` will default
|
||||||
|
to `"%Y-%m-%d %H:%M:%S"` and `"en"` (english) respectively.
|
||||||
|
|
||||||
|
@param formatString -- A string containing formatting tokens
|
||||||
|
@param locale -- The locale the time should be formatted in
|
||||||
|
@return string -- The formatting string
|
||||||
|
]=]
|
||||||
|
function DateTime.formatUniversalTime(
|
||||||
|
self: DateTime,
|
||||||
|
formatString: string?,
|
||||||
|
locale: Locale?
|
||||||
|
): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Formats this `DateTime` as an ISO 8601 date-time string.
|
||||||
|
|
||||||
|
Some examples of ISO 8601 date-time strings are:
|
||||||
|
|
||||||
|
- `2020-02-22T18:12:08Z`
|
||||||
|
- `2000-01-31T12:34:56+05:00`
|
||||||
|
- `1970-01-01T00:00:00.055Z`
|
||||||
|
|
||||||
|
@return string -- The ISO 8601 formatted string
|
||||||
|
]=]
|
||||||
|
function DateTime.toIsoDate(self: DateTime): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Extracts separated local date & time values from this `DateTime`.
|
||||||
|
|
||||||
|
The returned table contains the following values:
|
||||||
|
|
||||||
|
| Key | Type | Range |
|
||||||
|
|---------------|----------|----------------|
|
||||||
|
| `year` | `number` | `1400 -> 9999` |
|
||||||
|
| `month` | `number` | `1 -> 12` |
|
||||||
|
| `day` | `number` | `1 -> 31` |
|
||||||
|
| `hour` | `number` | `0 -> 23` |
|
||||||
|
| `minute` | `number` | `0 -> 59` |
|
||||||
|
| `second` | `number` | `0 -> 60` |
|
||||||
|
| `millisecond` | `number` | `0 -> 999` |
|
||||||
|
|
||||||
|
@return DateTimeValueReturns -- A table of DateTime values
|
||||||
|
]=]
|
||||||
|
function DateTime.toLocalTime(self: DateTime): DateTimeValueReturns
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Extracts separated UTC (universal) date & time values from this `DateTime`.
|
||||||
|
|
||||||
|
The returned table contains the following values:
|
||||||
|
|
||||||
|
| Key | Type | Range |
|
||||||
|
|---------------|----------|----------------|
|
||||||
|
| `year` | `number` | `1400 -> 9999` |
|
||||||
|
| `month` | `number` | `1 -> 12` |
|
||||||
|
| `day` | `number` | `1 -> 31` |
|
||||||
|
| `hour` | `number` | `0 -> 23` |
|
||||||
|
| `minute` | `number` | `0 -> 59` |
|
||||||
|
| `second` | `number` | `0 -> 60` |
|
||||||
|
| `millisecond` | `number` | `0 -> 999` |
|
||||||
|
|
||||||
|
@return DateTimeValueReturns -- A table of DateTime values
|
||||||
|
]=]
|
||||||
|
function DateTime.toUniversalTime(self: DateTime): DateTimeValueReturns
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
export type DateTime = typeof(DateTime)
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class DateTime
|
||||||
|
|
||||||
|
Built-in library for date & time
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local DateTime = require("@lune/datetime")
|
||||||
|
|
||||||
|
-- Creates a DateTime for the current exact moment in time
|
||||||
|
local now = DateTime.now()
|
||||||
|
|
||||||
|
-- Formats the current moment in time as an ISO 8601 string
|
||||||
|
print(now:toIsoDate())
|
||||||
|
|
||||||
|
-- Formats the current moment in time, using the local
|
||||||
|
-- time, the French locale, and the specified time string
|
||||||
|
print(now:formatLocalTime("%A, %d %B %Y", "fr"))
|
||||||
|
|
||||||
|
-- Returns a specific moment in time as a DateTime instance
|
||||||
|
local someDayInTheFuture = DateTime.fromLocalTime({
|
||||||
|
year = 3033,
|
||||||
|
month = 8,
|
||||||
|
day = 26,
|
||||||
|
hour = 16,
|
||||||
|
minute = 56,
|
||||||
|
second = 28,
|
||||||
|
millisecond = 892,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Extracts the current local date & time as separate values (same values as above table)
|
||||||
|
print(now:toLocalTime())
|
||||||
|
|
||||||
|
-- Returns a DateTime instance from a given float, where the whole
|
||||||
|
-- denotes the seconds and the fraction denotes the milliseconds
|
||||||
|
-- Note that the fraction for millis here is completely optional
|
||||||
|
DateTime.fromUnixTimestamp(871978212313.321)
|
||||||
|
|
||||||
|
-- Extracts the current universal (UTC) date & time as separate values
|
||||||
|
print(now:toUniversalTime())
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local dateTime = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Returns a `DateTime` representing the current moment in time.
|
||||||
|
|
||||||
|
@return DateTime -- The new DateTime object
|
||||||
|
]=]
|
||||||
|
function dateTime.now(): DateTime
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Creates a new `DateTime` from the given UNIX timestamp.
|
||||||
|
|
||||||
|
This timestamp may contain both a whole and fractional part -
|
||||||
|
where the fractional part denotes milliseconds / nanoseconds.
|
||||||
|
|
||||||
|
Example usage of fractions:
|
||||||
|
|
||||||
|
- `DateTime.fromUnixTimestamp(123456789.001)` - one millisecond
|
||||||
|
- `DateTime.fromUnixTimestamp(123456789.000000001)` - one nanosecond
|
||||||
|
|
||||||
|
Note that the fractional part has limited precision down to exactly
|
||||||
|
one nanosecond, any fraction that is more precise will get truncated.
|
||||||
|
|
||||||
|
@param unixTimestamp -- Seconds passed since the UNIX epoch
|
||||||
|
@return DateTime -- The new DateTime object
|
||||||
|
]=]
|
||||||
|
function dateTime.fromUnixTimestamp(unixTimestamp: number): DateTime
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Creates a new `DateTime` from the given date & time values table, in universal (UTC) time.
|
||||||
|
|
||||||
|
The given table must contain the following values:
|
||||||
|
|
||||||
|
| Key | Type | Range |
|
||||||
|
|----------|----------|----------------|
|
||||||
|
| `year` | `number` | `1400 -> 9999` |
|
||||||
|
| `month` | `number` | `1 -> 12` |
|
||||||
|
| `day` | `number` | `1 -> 31` |
|
||||||
|
| `hour` | `number` | `0 -> 23` |
|
||||||
|
| `minute` | `number` | `0 -> 59` |
|
||||||
|
| `second` | `number` | `0 -> 60` |
|
||||||
|
|
||||||
|
An additional `millisecond` value may also be included,
|
||||||
|
and should be within the range `0 -> 999`, but is optional.
|
||||||
|
|
||||||
|
Any non-integer values in the given table will be rounded down.
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
This constructor is fallible and may throw an error in the following situations:
|
||||||
|
|
||||||
|
- Date units (year, month, day) were given that produce an invalid date. For example, January 32nd or February 29th on a non-leap year.
|
||||||
|
|
||||||
|
@param values -- Table containing date & time values
|
||||||
|
@return DateTime -- The new DateTime object
|
||||||
|
]=]
|
||||||
|
function dateTime.fromUniversalTime(values: DateTimeValueArguments): DateTime
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Creates a new `DateTime` from the given date & time values table, in local time.
|
||||||
|
|
||||||
|
The given table must contain the following values:
|
||||||
|
|
||||||
|
| Key | Type | Range |
|
||||||
|
|----------|----------|----------------|
|
||||||
|
| `year` | `number` | `1400 -> 9999` |
|
||||||
|
| `month` | `number` | `1 -> 12` |
|
||||||
|
| `day` | `number` | `1 -> 31` |
|
||||||
|
| `hour` | `number` | `0 -> 23` |
|
||||||
|
| `minute` | `number` | `0 -> 59` |
|
||||||
|
| `second` | `number` | `0 -> 60` |
|
||||||
|
|
||||||
|
An additional `millisecond` value may also be included,
|
||||||
|
and should be within the range `0 -> 999`, but is optional.
|
||||||
|
|
||||||
|
Any non-integer values in the given table will be rounded down.
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
This constructor is fallible and may throw an error in the following situations:
|
||||||
|
|
||||||
|
- Date units (year, month, day) were given that produce an invalid date. For example, January 32nd or February 29th on a non-leap year.
|
||||||
|
|
||||||
|
@param values -- Table containing date & time values
|
||||||
|
@return DateTime -- The new DateTime object
|
||||||
|
]=]
|
||||||
|
function dateTime.fromLocalTime(values: DateTimeValueArguments): DateTime
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within DateTime
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Creates a new `DateTime` from an ISO 8601 date-time string.
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
This constructor is fallible and may throw an error if the given
|
||||||
|
string does not strictly follow the ISO 8601 date-time string format.
|
||||||
|
|
||||||
|
Some examples of valid ISO 8601 date-time strings are:
|
||||||
|
|
||||||
|
- `2020-02-22T18:12:08Z`
|
||||||
|
- `2000-01-31T12:34:56+05:00`
|
||||||
|
- `1970-01-01T00:00:00.055Z`
|
||||||
|
|
||||||
|
@param isoDate -- An ISO 8601 formatted string
|
||||||
|
@return DateTime -- The new DateTime object
|
||||||
|
]=]
|
||||||
|
function dateTime.fromIsoDate(isoDate: string): DateTime
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return dateTime
|
289
.lune/.lune-defs/fs.luau
Normal file
289
.lune/.lune-defs/fs.luau
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
local DateTime = require("./datetime")
|
||||||
|
type DateTime = DateTime.DateTime
|
||||||
|
|
||||||
|
export type MetadataKind = "file" | "dir" | "symlink"
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface MetadataPermissions
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Permissions for the given file or directory.
|
||||||
|
|
||||||
|
This is a dictionary that will contain the following values:
|
||||||
|
|
||||||
|
* `readOnly` - If the target path is read-only or not
|
||||||
|
]=]
|
||||||
|
export type MetadataPermissions = {
|
||||||
|
readOnly: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- FIXME: We lose doc comments here below in Metadata because of the union type
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface Metadata
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Metadata for the given file or directory.
|
||||||
|
|
||||||
|
This is a dictionary that will contain the following values:
|
||||||
|
|
||||||
|
* `kind` - If the target path is a `file`, `dir` or `symlink`
|
||||||
|
* `exists` - If the target path exists
|
||||||
|
* `createdAt` - The timestamp represented as a `DateTime` object at which the file or directory was created
|
||||||
|
* `modifiedAt` - The timestamp represented as a `DateTime` object at which the file or directory was last modified
|
||||||
|
* `accessedAt` - The timestamp represented as a `DateTime` object at which the file or directory was last accessed
|
||||||
|
* `permissions` - Current permissions for the file or directory
|
||||||
|
|
||||||
|
Note that timestamps are relative to the unix epoch, and
|
||||||
|
may not be accurate if the system clock is not accurate.
|
||||||
|
]=]
|
||||||
|
export type Metadata = {
|
||||||
|
kind: MetadataKind,
|
||||||
|
exists: true,
|
||||||
|
createdAt: DateTime,
|
||||||
|
modifiedAt: DateTime,
|
||||||
|
accessedAt: DateTime,
|
||||||
|
permissions: MetadataPermissions,
|
||||||
|
} | {
|
||||||
|
kind: nil,
|
||||||
|
exists: false,
|
||||||
|
createdAt: nil,
|
||||||
|
modifiedAt: nil,
|
||||||
|
accessedAt: nil,
|
||||||
|
permissions: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface WriteOptions
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Options for filesystem APIs what write to files and/or directories.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `overwrite` - If the target path should be overwritten or not, in the case that it already exists
|
||||||
|
]=]
|
||||||
|
export type WriteOptions = {
|
||||||
|
overwrite: boolean?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class FS
|
||||||
|
|
||||||
|
Built-in library for filesystem access
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
|
||||||
|
-- Reading a file
|
||||||
|
local myTextFile: string = fs.readFile("myFileName.txt")
|
||||||
|
|
||||||
|
-- Reading entries (files & dirs) in a directory
|
||||||
|
for _, entryName in fs.readDir("myDirName") do
|
||||||
|
if fs.isFile("myDirName/" .. entryName) then
|
||||||
|
print("Found file " .. entryName)
|
||||||
|
elseif fs.isDir("myDirName/" .. entryName) then
|
||||||
|
print("Found subdirectory " .. entryName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local fs = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Reads a file at `path`.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* `path` does not point to an existing file.
|
||||||
|
* The current process lacks permissions to read the file.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The path to the file to read
|
||||||
|
@return The contents of the file
|
||||||
|
]=]
|
||||||
|
function fs.readFile(path: string): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Reads entries in a directory at `path`.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* `path` does not point to an existing directory.
|
||||||
|
* The current process lacks permissions to read the contents of the directory.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The directory path to search in
|
||||||
|
@return A list of files & directories found
|
||||||
|
]=]
|
||||||
|
function fs.readDir(path: string): { string }
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Writes to a file at `path`.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The file's parent directory does not exist.
|
||||||
|
* The current process lacks permissions to write to the file.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The path of the file
|
||||||
|
@param contents The contents of the file
|
||||||
|
]=]
|
||||||
|
function fs.writeFile(path: string, contents: buffer | string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Creates a directory and its parent directories if they are missing.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* `path` already points to an existing file or directory.
|
||||||
|
* The current process lacks permissions to create the directory or its missing parents.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The directory to create
|
||||||
|
]=]
|
||||||
|
function fs.writeDir(path: string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Removes a file.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* `path` does not point to an existing file.
|
||||||
|
* The current process lacks permissions to remove the file.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The file to remove
|
||||||
|
]=]
|
||||||
|
function fs.removeFile(path: string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Removes a directory and all of its contents.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* `path` is not an existing and empty directory.
|
||||||
|
* The current process lacks permissions to remove the directory.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The directory to remove
|
||||||
|
]=]
|
||||||
|
function fs.removeDir(path: string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Gets metadata for the given path.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The current process lacks permissions to read at `path`.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The path to get metadata for
|
||||||
|
@return Metadata for the path
|
||||||
|
]=]
|
||||||
|
function fs.metadata(path: string): Metadata
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Checks if a given path is a file.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The current process lacks permissions to read at `path`.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The file path to check
|
||||||
|
@return If the path is a file or not
|
||||||
|
]=]
|
||||||
|
function fs.isFile(path: string): boolean
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Checks if a given path is a directory.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The current process lacks permissions to read at `path`.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param path The directory path to check
|
||||||
|
@return If the path is a directory or not
|
||||||
|
]=]
|
||||||
|
function fs.isDir(path: string): boolean
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Moves a file or directory to a new path.
|
||||||
|
|
||||||
|
Throws an error if a file or directory already exists at the target path.
|
||||||
|
This can be bypassed by passing `true` as the third argument, or a dictionary of options.
|
||||||
|
Refer to the documentation for `WriteOptions` for specific option keys and their values.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The current process lacks permissions to read at `from` or write at `to`.
|
||||||
|
* The new path exists on a different mount point.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param from The path to move from
|
||||||
|
@param to The path to move to
|
||||||
|
@param overwriteOrOptions Options for the target path, such as if should be overwritten if it already exists
|
||||||
|
]=]
|
||||||
|
function fs.move(from: string, to: string, overwriteOrOptions: (boolean | WriteOptions)?) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within FS
|
||||||
|
|
||||||
|
Copies a file or directory recursively to a new path.
|
||||||
|
|
||||||
|
Throws an error if a file or directory already exists at the target path.
|
||||||
|
This can be bypassed by passing `true` as the third argument, or a dictionary of options.
|
||||||
|
Refer to the documentation for `WriteOptions` for specific option keys and their values.
|
||||||
|
|
||||||
|
An error will be thrown in the following situations:
|
||||||
|
|
||||||
|
* The current process lacks permissions to read at `from` or write at `to`.
|
||||||
|
* Some other I/O error occurred.
|
||||||
|
|
||||||
|
@param from The path to copy from
|
||||||
|
@param to The path to copy to
|
||||||
|
@param overwriteOrOptions Options for the target path, such as if should be overwritten if it already exists
|
||||||
|
]=]
|
||||||
|
function fs.copy(from: string, to: string, overwriteOrOptions: (boolean | WriteOptions)?) end
|
||||||
|
|
||||||
|
return fs
|
123
.lune/.lune-defs/luau.luau
Normal file
123
.lune/.lune-defs/luau.luau
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
--[=[
|
||||||
|
@interface CompileOptions
|
||||||
|
@within Luau
|
||||||
|
|
||||||
|
The options passed to the luau compiler while compiling bytecode.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `optimizationLevel` - Sets the compiler option "optimizationLevel". Defaults to `1`.
|
||||||
|
* `coverageLevel` - Sets the compiler option "coverageLevel". Defaults to `0`.
|
||||||
|
* `debugLevel` - Sets the compiler option "debugLevel". Defaults to `1`.
|
||||||
|
|
||||||
|
Documentation regarding what these values represent can be found [here](https://github.com/Roblox/luau/blob/bd229816c0a82a8590395416c81c333087f541fd/Compiler/include/luacode.h#L13-L39).
|
||||||
|
]=]
|
||||||
|
export type CompileOptions = {
|
||||||
|
optimizationLevel: number?,
|
||||||
|
coverageLevel: number?,
|
||||||
|
debugLevel: number?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface LoadOptions
|
||||||
|
@within Luau
|
||||||
|
|
||||||
|
The options passed while loading a luau chunk from an arbitrary string, or bytecode.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `debugName` - The debug name of the closure. Defaults to `luau.load(...)`.
|
||||||
|
* `environment` - A custom environment to load the chunk in. Setting a custom environment will deoptimize the chunk and forcefully disable codegen. Defaults to the global environment.
|
||||||
|
* `injectGlobals` - Whether or not to inject globals in the custom environment. Has no effect if no custom environment is provided. Defaults to `true`.
|
||||||
|
* `codegenEnabled` - Whether or not to enable codegen. Defaults to `false`.
|
||||||
|
]=]
|
||||||
|
export type LoadOptions = {
|
||||||
|
debugName: string?,
|
||||||
|
environment: { [string]: any }?,
|
||||||
|
injectGlobals: boolean?,
|
||||||
|
codegenEnabled: boolean?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Luau
|
||||||
|
|
||||||
|
Built-in library for generating luau bytecode & functions.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local luau = require("@lune/luau")
|
||||||
|
|
||||||
|
local bytecode = luau.compile("print('Hello, World!')")
|
||||||
|
local callableFn = luau.load(bytecode)
|
||||||
|
|
||||||
|
-- Additionally, we can skip the bytecode generation and load a callable function directly from the code itself.
|
||||||
|
-- local callableFn = luau.load("print('Hello, World!')")
|
||||||
|
|
||||||
|
callableFn()
|
||||||
|
```
|
||||||
|
|
||||||
|
Since luau bytecode is highly compressible, it may also make sense to compress it using the `serde` library
|
||||||
|
while transmitting large amounts of it.
|
||||||
|
]=]
|
||||||
|
local luau = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Luau
|
||||||
|
|
||||||
|
Compiles sourcecode into Luau bytecode
|
||||||
|
|
||||||
|
An error will be thrown if the sourcecode given isn't valid Luau code.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local luau = require("@lune/luau")
|
||||||
|
|
||||||
|
-- Compile the source to some highly optimized bytecode
|
||||||
|
local bytecode = luau.compile("print('Hello, World!')", {
|
||||||
|
optimizationLevel = 2,
|
||||||
|
coverageLevel = 0,
|
||||||
|
debugLevel = 1,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
@param source The string that will be compiled into bytecode
|
||||||
|
@param compileOptions The options passed to the luau compiler that will output the bytecode
|
||||||
|
|
||||||
|
@return luau bytecode
|
||||||
|
]=]
|
||||||
|
function luau.compile(source: string, compileOptions: CompileOptions?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Luau
|
||||||
|
|
||||||
|
Generates a function from either bytecode or sourcecode
|
||||||
|
|
||||||
|
An error will be thrown if the sourcecode given isn't valid luau code.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local luau = require("@lune/luau")
|
||||||
|
|
||||||
|
local bytecode = luau.compile("print('Hello, World!')")
|
||||||
|
local callableFn = luau.load(bytecode, {
|
||||||
|
debugName = "'Hello, World'"
|
||||||
|
})
|
||||||
|
|
||||||
|
callableFn()
|
||||||
|
```
|
||||||
|
|
||||||
|
@param source Either luau bytecode or string source code
|
||||||
|
@param loadOptions The options passed to luau for loading the chunk
|
||||||
|
|
||||||
|
@return luau chunk
|
||||||
|
]=]
|
||||||
|
function luau.load(source: string, loadOptions: LoadOptions?): (...any) -> ...any
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return luau
|
321
.lune/.lune-defs/net.luau
Normal file
321
.lune/.lune-defs/net.luau
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | "PATCH"
|
||||||
|
|
||||||
|
type HttpQueryOrHeaderMap = { [string]: string | { string } }
|
||||||
|
export type HttpQueryMap = HttpQueryOrHeaderMap
|
||||||
|
export type HttpHeaderMap = HttpQueryOrHeaderMap
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface FetchParamsOptions
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Extra options for `FetchParams`.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `decompress` - If the request body should be automatically decompressed when possible. Defaults to `true`
|
||||||
|
]=]
|
||||||
|
export type FetchParamsOptions = {
|
||||||
|
decompress: boolean?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface FetchParams
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Parameters for sending network requests with `net.request`.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `url` - The URL to send a request to. This is always required
|
||||||
|
* `method` - The HTTP method verb, such as `"GET"`, `"POST"`, `"PATCH"`, `"PUT"`, or `"DELETE"`. Defaults to `"GET"`
|
||||||
|
* `body` - The request body
|
||||||
|
* `query` - A table of key-value pairs representing query parameters in the request path
|
||||||
|
* `headers` - A table of key-value pairs representing headers
|
||||||
|
* `options` - Extra options for things such as automatic decompression of response bodies
|
||||||
|
]=]
|
||||||
|
export type FetchParams = {
|
||||||
|
url: string,
|
||||||
|
method: HttpMethod?,
|
||||||
|
body: (string | buffer)?,
|
||||||
|
query: HttpQueryMap?,
|
||||||
|
headers: HttpHeaderMap?,
|
||||||
|
options: FetchParamsOptions?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface FetchResponse
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Response type for sending network requests with `net.request`.
|
||||||
|
|
||||||
|
This is a dictionary containing the following values:
|
||||||
|
|
||||||
|
* `ok` - If the status code is a canonical success status code, meaning within the range 200 -> 299
|
||||||
|
* `statusCode` - The status code returned for the request
|
||||||
|
* `statusMessage` - The canonical status message for the returned status code, such as `"Not Found"` for status code 404
|
||||||
|
* `headers` - A table of key-value pairs representing headers
|
||||||
|
* `body` - The request body, or an empty string if one was not given
|
||||||
|
]=]
|
||||||
|
export type FetchResponse = {
|
||||||
|
ok: boolean,
|
||||||
|
statusCode: number,
|
||||||
|
statusMessage: string,
|
||||||
|
headers: HttpHeaderMap,
|
||||||
|
body: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface ServeRequest
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Data type for requests in `net.serve`.
|
||||||
|
|
||||||
|
This is a dictionary containing the following values:
|
||||||
|
|
||||||
|
* `path` - The path being requested, relative to the root. Will be `/` if not specified
|
||||||
|
* `query` - A table of key-value pairs representing query parameters in the request path
|
||||||
|
* `method` - The HTTP method verb, such as `"GET"`, `"POST"`, `"PATCH"`, `"PUT"`, or `"DELETE"`. Will always be uppercase
|
||||||
|
* `headers` - A table of key-value pairs representing headers
|
||||||
|
* `body` - The request body, or an empty string if one was not given
|
||||||
|
]=]
|
||||||
|
export type ServeRequest = {
|
||||||
|
path: string,
|
||||||
|
query: { [string]: string? },
|
||||||
|
method: HttpMethod,
|
||||||
|
headers: { [string]: string },
|
||||||
|
body: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface ServeResponse
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Response type for requests in `net.serve`.
|
||||||
|
|
||||||
|
This is a dictionary that may contain one or more of the following values:
|
||||||
|
|
||||||
|
* `status` - The status code for the request, in the range `100` -> `599`
|
||||||
|
* `headers` - A table of key-value pairs representing headers
|
||||||
|
* `body` - The response body
|
||||||
|
]=]
|
||||||
|
export type ServeResponse = {
|
||||||
|
status: number?,
|
||||||
|
headers: { [string]: string }?,
|
||||||
|
body: (string | buffer)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServeHttpHandler = (request: ServeRequest) -> string | ServeResponse
|
||||||
|
type ServeWebSocketHandler = (socket: WebSocket) -> ()
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface ServeConfig
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Configuration for `net.serve`.
|
||||||
|
|
||||||
|
This may contain one of or more of the following values:
|
||||||
|
|
||||||
|
* `address` for setting the IP address to serve from. Defaults to the loopback interface (`http://localhost`).
|
||||||
|
* `handleRequest` for handling normal http requests, equivalent to just passing a function to `net.serve`
|
||||||
|
* `handleWebSocket` for handling web socket requests, which will receive a `WebSocket` object as its first and only parameter
|
||||||
|
|
||||||
|
When setting `address`, the `handleRequest` callback must also be defined.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
net.serve(8080, {
|
||||||
|
address = "http://0.0.0.0",
|
||||||
|
handleRequest = function(request)
|
||||||
|
return {
|
||||||
|
status = 200,
|
||||||
|
body = "Echo:\n" .. request.body,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
})
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
export type ServeConfig = {
|
||||||
|
address: string?,
|
||||||
|
handleRequest: ServeHttpHandler?,
|
||||||
|
handleWebSocket: ServeWebSocketHandler?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface ServeHandle
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
A handle to a currently running web server, containing a single `stop` function to gracefully shut down the web server.
|
||||||
|
]=]
|
||||||
|
export type ServeHandle = {
|
||||||
|
stop: () -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface WebSocket
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
A reference to a web socket connection.
|
||||||
|
|
||||||
|
The web socket may be in either an "open" or a "closed" state, changing its current behavior.
|
||||||
|
|
||||||
|
When open:
|
||||||
|
|
||||||
|
* Any function on the socket such as `send`, `next` or `close` can be called without erroring
|
||||||
|
* `next` can be called to yield until the next message is received or the socket becomes closed
|
||||||
|
|
||||||
|
When closed:
|
||||||
|
|
||||||
|
* `next` will no longer return any message(s) and instead instantly return nil
|
||||||
|
* `send` will throw an error stating that the socket has been closed
|
||||||
|
|
||||||
|
Once the websocket has been closed, `closeCode` will no longer be nil, and will be populated with a close
|
||||||
|
code according to the [WebSocket specification](https://www.iana.org/assignments/websocket/websocket.xhtml).
|
||||||
|
This will be an integer between 1000 and 4999, where 1000 is the canonical code for normal, error-free closure.
|
||||||
|
]=]
|
||||||
|
export type WebSocket = {
|
||||||
|
closeCode: number?,
|
||||||
|
close: (code: number?) -> (),
|
||||||
|
send: (message: (string | buffer)?, asBinaryMessage: boolean?) -> (),
|
||||||
|
next: () -> string?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Net
|
||||||
|
|
||||||
|
|
||||||
|
Built-in library for network access
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local net = require("@lune/net")
|
||||||
|
|
||||||
|
-- Sending a web request
|
||||||
|
local response = net.request("https://www.google.com")
|
||||||
|
print(response.ok)
|
||||||
|
print(response.statusCode, response.statusMessage)
|
||||||
|
print(response.headers)
|
||||||
|
|
||||||
|
-- Using a JSON web API
|
||||||
|
local response = net.request({
|
||||||
|
url = "https://dummyjson.com/products/add",
|
||||||
|
method = "POST",
|
||||||
|
headers = { ["Content-Type"] = "application/json" },
|
||||||
|
body = net.jsonEncode({
|
||||||
|
title = "Cool Pencil",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
local product = net.jsonDecode(response.body)
|
||||||
|
print(product.id, "-", product.title)
|
||||||
|
|
||||||
|
-- Starting up a webserver
|
||||||
|
net.serve(8080, function(request)
|
||||||
|
return {
|
||||||
|
status = 200,
|
||||||
|
body = "Echo:\n" .. request.body,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local net = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Sends an HTTP request using the given url and / or parameters, and returns a dictionary that describes the response received.
|
||||||
|
|
||||||
|
Only throws an error if a miscellaneous network or I/O error occurs, never for unsuccessful status codes.
|
||||||
|
|
||||||
|
@param config The URL or request config to use
|
||||||
|
@return A dictionary representing the response for the request
|
||||||
|
]=]
|
||||||
|
function net.request(config: string | FetchParams): FetchResponse
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Connects to a web socket at the given URL.
|
||||||
|
|
||||||
|
Throws an error if the server at the given URL does not support
|
||||||
|
web sockets, or if a miscellaneous network or I/O error occurs.
|
||||||
|
|
||||||
|
@param url The URL to connect to
|
||||||
|
@return A web socket handle
|
||||||
|
]=]
|
||||||
|
function net.socket(url: string): WebSocket
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
|
||||||
|
Creates an HTTP server that listens on the given `port`.
|
||||||
|
|
||||||
|
This will ***not*** block and will keep listening for requests on the given `port`
|
||||||
|
until the `stop` function on the returned `ServeHandle` has been called.
|
||||||
|
|
||||||
|
@param port The port to use for the server
|
||||||
|
@param handlerOrConfig The handler function or config to use for the server
|
||||||
|
]=]
|
||||||
|
function net.serve(port: number, handlerOrConfig: ServeHttpHandler | ServeConfig): ServeHandle
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Encodes the given value as JSON.
|
||||||
|
|
||||||
|
@param value The value to encode as JSON
|
||||||
|
@param pretty If the encoded JSON string should include newlines and spaces. Defaults to false
|
||||||
|
@return The encoded JSON string
|
||||||
|
]=]
|
||||||
|
function net.jsonEncode(value: any, pretty: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Decodes the given JSON string into a lua value.
|
||||||
|
|
||||||
|
@param encoded The JSON string to decode
|
||||||
|
@return The decoded lua value
|
||||||
|
]=]
|
||||||
|
function net.jsonDecode(encoded: string): any
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Encodes the given string using URL encoding.
|
||||||
|
|
||||||
|
@param s The string to encode
|
||||||
|
@param binary If the string should be treated as binary data and/or is not valid utf-8. Defaults to false
|
||||||
|
@return The encoded string
|
||||||
|
]=]
|
||||||
|
function net.urlEncode(s: string, binary: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Net
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Decodes the given string using URL decoding.
|
||||||
|
|
||||||
|
@param s The string to decode
|
||||||
|
@param binary If the string should be treated as binary data and/or is not valid utf-8. Defaults to false
|
||||||
|
@return The decoded string
|
||||||
|
]=]
|
||||||
|
function net.urlDecode(s: string, binary: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return net
|
182
.lune/.lune-defs/process.luau
Normal file
182
.lune/.lune-defs/process.luau
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
export type OS = "linux" | "macos" | "windows"
|
||||||
|
export type Arch = "x86_64" | "aarch64"
|
||||||
|
|
||||||
|
export type SpawnOptionsStdioKind = "default" | "inherit" | "forward" | "none"
|
||||||
|
export type SpawnOptionsStdio = {
|
||||||
|
stdout: SpawnOptionsStdioKind?,
|
||||||
|
stderr: SpawnOptionsStdioKind?,
|
||||||
|
stdin: string?,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface SpawnOptions
|
||||||
|
@within Process
|
||||||
|
|
||||||
|
A dictionary of options for `process.spawn`, with the following available values:
|
||||||
|
|
||||||
|
* `cwd` - The current working directory for the process
|
||||||
|
* `env` - Extra environment variables to give to the process
|
||||||
|
* `shell` - Whether to run in a shell or not - set to `true` to run using the default shell, or a string to run using a specific shell
|
||||||
|
* `stdio` - How to treat output and error streams from the child process - see `SpawnOptionsStdioKind` and `SpawnOptionsStdio` for more info
|
||||||
|
* `stdin` - Optional standard input to pass to spawned child process
|
||||||
|
]=]
|
||||||
|
export type SpawnOptions = {
|
||||||
|
cwd: string?,
|
||||||
|
env: { [string]: string }?,
|
||||||
|
shell: (boolean | string)?,
|
||||||
|
stdio: (SpawnOptionsStdioKind | SpawnOptionsStdio)?,
|
||||||
|
stdin: string?, -- TODO: Remove this since it is now available in stdio above, breaking change
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@interface SpawnResult
|
||||||
|
@within Process
|
||||||
|
|
||||||
|
Result type for child processes in `process.spawn`.
|
||||||
|
|
||||||
|
This is a dictionary containing the following values:
|
||||||
|
|
||||||
|
* `ok` - If the child process exited successfully or not, meaning the exit code was zero or not set
|
||||||
|
* `code` - The exit code set by the child process, or 0 if one was not set
|
||||||
|
* `stdout` - The full contents written to stdout by the child process, or an empty string if nothing was written
|
||||||
|
* `stderr` - The full contents written to stderr by the child process, or an empty string if nothing was written
|
||||||
|
]=]
|
||||||
|
export type SpawnResult = {
|
||||||
|
ok: boolean,
|
||||||
|
code: number,
|
||||||
|
stdout: string,
|
||||||
|
stderr: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Process
|
||||||
|
|
||||||
|
Built-in functions for the current process & child processes
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local process = require("@lune/process")
|
||||||
|
|
||||||
|
-- Getting the arguments passed to the Lune script
|
||||||
|
for index, arg in process.args do
|
||||||
|
print("Process argument #" .. tostring(index) .. ": " .. arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Getting the currently available environment variables
|
||||||
|
local PORT: string? = process.env.PORT
|
||||||
|
local HOME: string? = process.env.HOME
|
||||||
|
for name, value in process.env do
|
||||||
|
print("Environment variable " .. name .. " is set to " .. value)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Getting the current os and processor architecture
|
||||||
|
print("Running " .. process.os .. " on " .. process.arch .. "!")
|
||||||
|
|
||||||
|
-- Spawning a child process
|
||||||
|
local result = process.spawn("program", {
|
||||||
|
"cli argument",
|
||||||
|
"other cli argument"
|
||||||
|
})
|
||||||
|
if result.ok then
|
||||||
|
print(result.stdout)
|
||||||
|
else
|
||||||
|
print(result.stderr)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local process = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
@prop os OS
|
||||||
|
@tag read_only
|
||||||
|
|
||||||
|
The current operating system being used.
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
* `"linux"`
|
||||||
|
* `"macos"`
|
||||||
|
* `"windows"`
|
||||||
|
]=]
|
||||||
|
process.os = (nil :: any) :: OS
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
@prop arch Arch
|
||||||
|
@tag read_only
|
||||||
|
|
||||||
|
The architecture of the processor currently being used.
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
* `"x86_64"`
|
||||||
|
* `"aarch64"`
|
||||||
|
]=]
|
||||||
|
process.arch = (nil :: any) :: Arch
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
@prop args { string }
|
||||||
|
@tag read_only
|
||||||
|
|
||||||
|
The arguments given when running the Lune script.
|
||||||
|
]=]
|
||||||
|
process.args = (nil :: any) :: { string }
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
@prop cwd string
|
||||||
|
@tag read_only
|
||||||
|
|
||||||
|
The current working directory in which the Lune script is running.
|
||||||
|
]=]
|
||||||
|
process.cwd = (nil :: any) :: string
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
@prop env { [string]: string? }
|
||||||
|
@tag read_write
|
||||||
|
|
||||||
|
Current environment variables for this process.
|
||||||
|
|
||||||
|
Setting a value on this table will set the corresponding environment variable.
|
||||||
|
]=]
|
||||||
|
process.env = (nil :: any) :: { [string]: string? }
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
|
||||||
|
Exits the currently running script as soon as possible with the given exit code.
|
||||||
|
|
||||||
|
Exit code 0 is treated as a successful exit, any other value is treated as an error.
|
||||||
|
|
||||||
|
Setting the exit code using this function will override any otherwise automatic exit code.
|
||||||
|
|
||||||
|
@param code The exit code to set
|
||||||
|
]=]
|
||||||
|
function process.exit(code: number?): never
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Process
|
||||||
|
|
||||||
|
Spawns a child process that will run the program `program`, and returns a dictionary that describes the final status and ouput of the child process.
|
||||||
|
|
||||||
|
The second argument, `params`, can be passed as a list of string parameters to give to the program.
|
||||||
|
|
||||||
|
The third argument, `options`, can be passed as a dictionary of options to give to the child process.
|
||||||
|
Refer to the documentation for `SpawnOptions` for specific option keys and their values.
|
||||||
|
|
||||||
|
@param program The program to spawn as a child process
|
||||||
|
@param params Additional parameters to pass to the program
|
||||||
|
@param options A dictionary of options for the child process
|
||||||
|
@return A dictionary representing the result of the child process
|
||||||
|
]=]
|
||||||
|
function process.spawn(program: string, params: { string }?, options: SpawnOptions?): SpawnResult
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return process
|
218
.lune/.lune-defs/regex.luau
Normal file
218
.lune/.lune-defs/regex.luau
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
--[=[
|
||||||
|
@class RegexMatch
|
||||||
|
|
||||||
|
A match from a regular expression.
|
||||||
|
|
||||||
|
Contains the following values:
|
||||||
|
|
||||||
|
- `start` -- The start index of the match in the original string.
|
||||||
|
- `finish` -- The end index of the match in the original string.
|
||||||
|
- `text` -- The text that was matched.
|
||||||
|
- `len` -- The length of the text that was matched.
|
||||||
|
]=]
|
||||||
|
local RegexMatch = {
|
||||||
|
start = 0,
|
||||||
|
finish = 0,
|
||||||
|
text = "",
|
||||||
|
len = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegexMatch = typeof(RegexMatch)
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class RegexCaptures
|
||||||
|
|
||||||
|
Captures from a regular expression.
|
||||||
|
]=]
|
||||||
|
local RegexCaptures = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within RegexCaptures
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Returns the match at the given index, if one exists.
|
||||||
|
|
||||||
|
@param index -- The index of the match to get
|
||||||
|
@return RegexMatch -- The match, if one exists
|
||||||
|
]=]
|
||||||
|
function RegexCaptures.get(self: RegexCaptures, index: number): RegexMatch?
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within RegexCaptures
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Returns the match for the given named match group, if one exists.
|
||||||
|
|
||||||
|
@param group -- The name of the group to get
|
||||||
|
@return RegexMatch -- The match, if one exists
|
||||||
|
]=]
|
||||||
|
function RegexCaptures.group(self: RegexCaptures, group: string): RegexMatch?
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within RegexCaptures
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Formats the captures using the given format string.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local regex = require("@lune/regex")
|
||||||
|
|
||||||
|
local re = regex.new("(?<day>[0-9]{2})-(?<month>[0-9]{2})-(?<year>[0-9]{4})")
|
||||||
|
|
||||||
|
local caps = re:captures("On 14-03-2010, I became a Tenneessee lamb.");
|
||||||
|
assert(caps ~= nil, "Example pattern should match example text")
|
||||||
|
|
||||||
|
local formatted = caps:format("year=$year, month=$month, day=$day")
|
||||||
|
print(formatted) -- "year=2010, month=03, day=14"
|
||||||
|
```
|
||||||
|
|
||||||
|
@param format -- The format string to use
|
||||||
|
@return string -- The formatted string
|
||||||
|
]=]
|
||||||
|
function RegexCaptures.format(self: RegexCaptures, format: string): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
export type RegexCaptures = typeof(RegexCaptures)
|
||||||
|
|
||||||
|
local Regex = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Check if the given text matches the regular expression.
|
||||||
|
|
||||||
|
This method may be slightly more efficient than calling `find`
|
||||||
|
if you only need to know if the text matches the pattern.
|
||||||
|
|
||||||
|
@param text -- The text to search
|
||||||
|
@return boolean -- Whether the text matches the pattern
|
||||||
|
]=]
|
||||||
|
function Regex.isMatch(self: Regex, text: string): boolean
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Finds the first match in the given text.
|
||||||
|
|
||||||
|
Returns `nil` if no match was found.
|
||||||
|
|
||||||
|
@param text -- The text to search
|
||||||
|
@return RegexMatch? -- The match object
|
||||||
|
]=]
|
||||||
|
function Regex.find(self: Regex, text: string): RegexMatch?
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Finds all matches in the given text as a `RegexCaptures` object.
|
||||||
|
|
||||||
|
Returns `nil` if no matches are found.
|
||||||
|
|
||||||
|
@param text -- The text to search
|
||||||
|
@return RegexCaptures? -- The captures object
|
||||||
|
]=]
|
||||||
|
function Regex.captures(self: Regex, text: string): RegexCaptures?
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Splits the given text using the regular expression.
|
||||||
|
|
||||||
|
@param text -- The text to split
|
||||||
|
@return { string } -- The split text
|
||||||
|
]=]
|
||||||
|
function Regex.split(self: Regex, text: string): { string }
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Replaces the first match in the given text with the given replacer string.
|
||||||
|
|
||||||
|
@param haystack -- The text to search
|
||||||
|
@param replacer -- The string to replace matches with
|
||||||
|
@return string -- The text with the first match replaced
|
||||||
|
]=]
|
||||||
|
function Regex.replace(self: Regex, haystack: string, replacer: string): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Method
|
||||||
|
|
||||||
|
Replaces all matches in the given text with the given replacer string.
|
||||||
|
|
||||||
|
@param haystack -- The text to search
|
||||||
|
@param replacer -- The string to replace matches with
|
||||||
|
@return string -- The text with all matches replaced
|
||||||
|
]=]
|
||||||
|
function Regex.replaceAll(self: Regex, haystack: string, replacer: string): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Regex = typeof(Regex)
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Regex
|
||||||
|
|
||||||
|
Built-in library for regular expressions
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local Regex = require("@lune/regex")
|
||||||
|
|
||||||
|
local re = Regex.new("hello")
|
||||||
|
|
||||||
|
if re:isMatch("hello, world!") then
|
||||||
|
print("Matched!")
|
||||||
|
end
|
||||||
|
|
||||||
|
local caps = re:captures("hello, world! hello, again!")
|
||||||
|
|
||||||
|
print(#caps) -- 2
|
||||||
|
print(caps:get(1)) -- "hello"
|
||||||
|
print(caps:get(2)) -- "hello"
|
||||||
|
print(caps:get(3)) -- nil
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local regex = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Regex
|
||||||
|
@tag Constructor
|
||||||
|
|
||||||
|
Creates a new `Regex` from a given string pattern.
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
This constructor throws an error if the given pattern is invalid.
|
||||||
|
|
||||||
|
@param pattern -- The string pattern to use
|
||||||
|
@return Regex -- The new Regex object
|
||||||
|
]=]
|
||||||
|
function regex.new(pattern: string): Regex
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return regex
|
507
.lune/.lune-defs/roblox.luau
Normal file
507
.lune/.lune-defs/roblox.luau
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
export type DatabaseScriptability = "None" | "Custom" | "Read" | "ReadWrite" | "Write"
|
||||||
|
|
||||||
|
export type DatabasePropertyTag =
|
||||||
|
"Deprecated"
|
||||||
|
| "Hidden"
|
||||||
|
| "NotBrowsable"
|
||||||
|
| "NotReplicated"
|
||||||
|
| "NotScriptable"
|
||||||
|
| "ReadOnly"
|
||||||
|
| "WriteOnly"
|
||||||
|
|
||||||
|
export type DatabaseClassTag =
|
||||||
|
"Deprecated"
|
||||||
|
| "NotBrowsable"
|
||||||
|
| "NotCreatable"
|
||||||
|
| "NotReplicated"
|
||||||
|
| "PlayerReplicated"
|
||||||
|
| "Service"
|
||||||
|
| "Settings"
|
||||||
|
| "UserSettings"
|
||||||
|
|
||||||
|
export type DatabaseProperty = {
|
||||||
|
--[=[
|
||||||
|
The name of the property.
|
||||||
|
]=]
|
||||||
|
Name: string,
|
||||||
|
--[=[
|
||||||
|
The datatype of the property.
|
||||||
|
|
||||||
|
For normal datatypes this will be a string such as `string`, `Color3`, ...
|
||||||
|
|
||||||
|
For enums this will be a string formatted as `Enum.EnumName`.
|
||||||
|
]=]
|
||||||
|
Datatype: string,
|
||||||
|
--[=[
|
||||||
|
The scriptability of this property, meaning if it can be written / read at runtime.
|
||||||
|
|
||||||
|
All properties are writable and readable in Lune even if scriptability is not.
|
||||||
|
]=]
|
||||||
|
Scriptability: DatabaseScriptability,
|
||||||
|
--[=[
|
||||||
|
Tags describing the property.
|
||||||
|
|
||||||
|
These include information such as if the property can be replicated to players
|
||||||
|
at runtime, if the property should be hidden in Roblox Studio, and more.
|
||||||
|
]=]
|
||||||
|
Tags: { DatabasePropertyTag },
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatabaseClass = {
|
||||||
|
--[=[
|
||||||
|
The name of the class.
|
||||||
|
]=]
|
||||||
|
Name: string,
|
||||||
|
--[=[
|
||||||
|
The superclass (parent class) of this class.
|
||||||
|
|
||||||
|
May be nil if no parent class exists.
|
||||||
|
]=]
|
||||||
|
Superclass: string?,
|
||||||
|
--[=[
|
||||||
|
Known properties for this class.
|
||||||
|
]=]
|
||||||
|
Properties: { [string]: DatabaseProperty },
|
||||||
|
--[=[
|
||||||
|
Default values for properties of this class.
|
||||||
|
|
||||||
|
Note that these default properties use Lune's built-in datatype
|
||||||
|
userdatas, and that if there is a new datatype that Lune does
|
||||||
|
not yet know about, it may be missing from this table.
|
||||||
|
]=]
|
||||||
|
DefaultProperties: { [string]: any },
|
||||||
|
--[=[
|
||||||
|
Tags describing the class.
|
||||||
|
|
||||||
|
These include information such as if the class can be replicated
|
||||||
|
to players at runtime, and top-level class categories.
|
||||||
|
]=]
|
||||||
|
Tags: { DatabaseClassTag },
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DatabaseEnum = {
|
||||||
|
--[=[
|
||||||
|
The name of this enum, for example `PartType` or `UserInputState`.
|
||||||
|
]=]
|
||||||
|
Name: string,
|
||||||
|
--[=[
|
||||||
|
Members of this enum.
|
||||||
|
|
||||||
|
Note that this is a direct map of name -> enum values,
|
||||||
|
and does not actually use the EnumItem datatype itself.
|
||||||
|
]=]
|
||||||
|
Items: { [string]: number },
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Database = {
|
||||||
|
--[=[
|
||||||
|
The current version of the reflection database.
|
||||||
|
|
||||||
|
This will follow the format `x.y.z.w`, which most commonly looks something like `0.567.0.123456789`
|
||||||
|
]=]
|
||||||
|
Version: string,
|
||||||
|
--[=[
|
||||||
|
Retrieves a list of all currently known class names.
|
||||||
|
]=]
|
||||||
|
GetClassNames: (self: Database) -> { string },
|
||||||
|
--[=[
|
||||||
|
Retrieves a list of all currently known enum names.
|
||||||
|
]=]
|
||||||
|
GetEnumNames: (self: Database) -> { string },
|
||||||
|
--[=[
|
||||||
|
Gets a class with the exact given name, if one exists.
|
||||||
|
]=]
|
||||||
|
GetClass: (self: Database, name: string) -> DatabaseClass?,
|
||||||
|
--[=[
|
||||||
|
Gets an enum with the exact given name, if one exists.
|
||||||
|
]=]
|
||||||
|
GetEnum: (self: Database, name: string) -> DatabaseEnum?,
|
||||||
|
--[=[
|
||||||
|
Finds a class with the given name.
|
||||||
|
|
||||||
|
This will use case-insensitive matching and ignore leading and trailing whitespace.
|
||||||
|
]=]
|
||||||
|
FindClass: (self: Database, name: string) -> DatabaseClass?,
|
||||||
|
--[=[
|
||||||
|
Finds an enum with the given name.
|
||||||
|
|
||||||
|
This will use case-insensitive matching and ignore leading and trailing whitespace.
|
||||||
|
]=]
|
||||||
|
FindEnum: (self: Database, name: string) -> DatabaseEnum?,
|
||||||
|
}
|
||||||
|
|
||||||
|
type InstanceProperties = {
|
||||||
|
Parent: Instance?,
|
||||||
|
ClassName: string,
|
||||||
|
Name: string,
|
||||||
|
-- FIXME: This breaks intellisense, but we need some way to access
|
||||||
|
-- instance properties without casting the entire instance to any...
|
||||||
|
-- [string]: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
type InstanceMetatable = {
|
||||||
|
Clone: (self: Instance) -> Instance,
|
||||||
|
Destroy: (self: Instance) -> (),
|
||||||
|
ClearAllChildren: (self: Instance) -> (),
|
||||||
|
|
||||||
|
GetChildren: (self: Instance) -> { Instance },
|
||||||
|
GetDebugId: (self: Instance) -> string,
|
||||||
|
GetDescendants: (self: Instance) -> { Instance },
|
||||||
|
GetFullName: (self: Instance) -> string,
|
||||||
|
|
||||||
|
FindFirstAncestor: (self: Instance, name: string) -> Instance?,
|
||||||
|
FindFirstAncestorOfClass: (self: Instance, className: string) -> Instance?,
|
||||||
|
FindFirstAncestorWhichIsA: (self: Instance, className: string) -> Instance?,
|
||||||
|
FindFirstChild: (self: Instance, name: string, recursive: boolean?) -> Instance?,
|
||||||
|
FindFirstChildOfClass: (self: Instance, className: string, recursive: boolean?) -> Instance?,
|
||||||
|
FindFirstChildWhichIsA: (self: Instance, className: string, recursive: boolean?) -> Instance?,
|
||||||
|
|
||||||
|
IsA: (self: Instance, className: string) -> boolean,
|
||||||
|
IsAncestorOf: (self: Instance, descendant: Instance) -> boolean,
|
||||||
|
IsDescendantOf: (self: Instance, ancestor: Instance) -> boolean,
|
||||||
|
|
||||||
|
GetAttribute: (self: Instance, name: string) -> any,
|
||||||
|
GetAttributes: (self: Instance) -> { [string]: any },
|
||||||
|
SetAttribute: (self: Instance, name: string, value: any) -> (),
|
||||||
|
|
||||||
|
GetTags: (self: Instance) -> { string },
|
||||||
|
HasTag: (self: Instance, name: string) -> boolean,
|
||||||
|
AddTag: (self: Instance, name: string) -> (),
|
||||||
|
RemoveTag: (self: Instance, name: string) -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Instance = typeof(setmetatable(
|
||||||
|
(nil :: any) :: InstanceProperties,
|
||||||
|
(nil :: any) :: { __index: InstanceMetatable }
|
||||||
|
))
|
||||||
|
|
||||||
|
export type DataModelProperties = {}
|
||||||
|
export type DataModelMetatable = {
|
||||||
|
GetService: (self: DataModel, name: string) -> Instance,
|
||||||
|
FindService: (self: DataModel, name: string) -> Instance?,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DataModel =
|
||||||
|
Instance
|
||||||
|
& typeof(setmetatable(
|
||||||
|
(nil :: any) :: DataModelProperties,
|
||||||
|
(nil :: any) :: { __index: DataModelMetatable }
|
||||||
|
))
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Roblox
|
||||||
|
|
||||||
|
Built-in library for manipulating Roblox place & model files
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
-- Reading a place file
|
||||||
|
local placeFile = fs.readFile("myPlaceFile.rbxl")
|
||||||
|
local game = roblox.deserializePlace(placeFile)
|
||||||
|
|
||||||
|
-- Manipulating and reading instances - just like in Roblox!
|
||||||
|
local workspace = game:GetService("Workspace")
|
||||||
|
for _, child in workspace:GetChildren() do
|
||||||
|
print("Found child " .. child.Name .. " of class " .. child.ClassName)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Writing a place file
|
||||||
|
local newPlaceFile = roblox.serializePlace(game)
|
||||||
|
fs.writeFile("myPlaceFile.rbxl", newPlaceFile)
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local roblox = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Deserializes a place into a DataModel instance.
|
||||||
|
|
||||||
|
This function accepts a string of contents, *not* a file path.
|
||||||
|
If reading a place file from a file path is desired, `fs.readFile`
|
||||||
|
can be used and the resulting string may be passed to this function.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local placeFile = fs.readFile("filePath.rbxl")
|
||||||
|
local game = roblox.deserializePlace(placeFile)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param contents The contents of the place to read
|
||||||
|
]=]
|
||||||
|
function roblox.deserializePlace(contents: string): DataModel
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Deserializes a model into an array of instances.
|
||||||
|
|
||||||
|
This function accepts a string of contents, *not* a file path.
|
||||||
|
If reading a model file from a file path is desired, `fs.readFile`
|
||||||
|
can be used and the resulting string may be passed to this function.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local modelFile = fs.readFile("filePath.rbxm")
|
||||||
|
local instances = roblox.deserializeModel(modelFile)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param contents The contents of the model to read
|
||||||
|
]=]
|
||||||
|
function roblox.deserializeModel(contents: string): { Instance }
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Serializes a place from a DataModel instance.
|
||||||
|
|
||||||
|
This string can then be written to a file, or sent over the network.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local placeFile = roblox.serializePlace(game)
|
||||||
|
fs.writeFile("filePath.rbxl", placeFile)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param dataModel The DataModel for the place to serialize
|
||||||
|
@param xml If the place should be serialized as xml or not. Defaults to `false`, meaning the place gets serialized using the binary format and not xml.
|
||||||
|
]=]
|
||||||
|
function roblox.serializePlace(dataModel: DataModel, xml: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Serializes one or more instances as a model.
|
||||||
|
|
||||||
|
This string can then be written to a file, or sent over the network.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local modelFile = roblox.serializeModel({ instance1, instance2, ... })
|
||||||
|
fs.writeFile("filePath.rbxm", modelFile)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param instances The array of instances to serialize
|
||||||
|
@param xml If the model should be serialized as xml or not. Defaults to `false`, meaning the model gets serialized using the binary format and not xml.
|
||||||
|
]=]
|
||||||
|
function roblox.serializeModel(instances: { Instance }, xml: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Gets the current auth cookie, for usage with Roblox web APIs.
|
||||||
|
|
||||||
|
Note that this auth cookie is formatted for use as a "Cookie" header,
|
||||||
|
and that it contains restrictions so that it may only be used for
|
||||||
|
official Roblox endpoints. To get the raw cookie value without any
|
||||||
|
additional formatting, you can pass `true` as the first and only parameter.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
local net = require("@lune/net")
|
||||||
|
|
||||||
|
local cookie = roblox.getAuthCookie()
|
||||||
|
assert(cookie ~= nil, "Failed to get roblox auth cookie")
|
||||||
|
|
||||||
|
local myPrivatePlaceId = 1234567890
|
||||||
|
|
||||||
|
local response = net.request({
|
||||||
|
url = "https://assetdelivery.roblox.com/v2/assetId/" .. tostring(myPrivatePlaceId),
|
||||||
|
headers = {
|
||||||
|
Cookie = cookie,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local responseTable = net.jsonDecode(response.body)
|
||||||
|
local responseLocation = responseTable.locations[1].location
|
||||||
|
print("Download link to place: " .. responseLocation)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param raw If the cookie should be returned as a pure value or not. Defaults to false
|
||||||
|
]=]
|
||||||
|
function roblox.getAuthCookie(raw: boolean?): string?
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Gets the bundled reflection database.
|
||||||
|
|
||||||
|
This database contains information about Roblox enums, classes, and their properties.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local db = roblox.getReflectionDatabase()
|
||||||
|
|
||||||
|
print("There are", #db:GetClassNames(), "classes in the reflection database")
|
||||||
|
|
||||||
|
print("All base instance properties:")
|
||||||
|
|
||||||
|
local class = db:GetClass("Instance")
|
||||||
|
for name, prop in class.Properties do
|
||||||
|
print(string.format(
|
||||||
|
"- %s with datatype %s and default value %s",
|
||||||
|
prop.Name,
|
||||||
|
prop.Datatype,
|
||||||
|
tostring(class.DefaultProperties[prop.Name])
|
||||||
|
))
|
||||||
|
end
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
function roblox.getReflectionDatabase(): Database
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
|
||||||
|
Implements a property for all instances of the given `className`.
|
||||||
|
|
||||||
|
This takes into account class hierarchies, so implementing a property
|
||||||
|
for the `BasePart` class will also implement it for `Part` and others,
|
||||||
|
unless a more specific implementation is added to the `Part` class directly.
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
The given `getter` callback will be called each time the property is
|
||||||
|
indexed, with the instance as its one and only argument. The `setter`
|
||||||
|
callback, if given, will be called each time the property should be set,
|
||||||
|
with the instance as the first argument and the property value as second.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local part = roblox.Instance.new("Part")
|
||||||
|
|
||||||
|
local propertyValues = {}
|
||||||
|
roblox.implementProperty(
|
||||||
|
"BasePart",
|
||||||
|
"CoolProp",
|
||||||
|
function(instance)
|
||||||
|
if propertyValues[instance] == nil then
|
||||||
|
propertyValues[instance] = 0
|
||||||
|
end
|
||||||
|
propertyValues[instance] += 1
|
||||||
|
return propertyValues[instance]
|
||||||
|
end,
|
||||||
|
function(instance, value)
|
||||||
|
propertyValues[instance] = value
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
print(part.CoolProp) --> 1
|
||||||
|
print(part.CoolProp) --> 2
|
||||||
|
print(part.CoolProp) --> 3
|
||||||
|
|
||||||
|
part.CoolProp = 10
|
||||||
|
|
||||||
|
print(part.CoolProp) --> 11
|
||||||
|
print(part.CoolProp) --> 12
|
||||||
|
print(part.CoolProp) --> 13
|
||||||
|
```
|
||||||
|
|
||||||
|
@param className The class to implement the property for.
|
||||||
|
@param propertyName The name of the property to implement.
|
||||||
|
@param getter The function which will be called to get the property value when indexed.
|
||||||
|
@param setter The function which will be called to set the property value when indexed. Defaults to a function that will error with a message saying the property is read-only.
|
||||||
|
]=]
|
||||||
|
function roblox.implementProperty<T>(
|
||||||
|
className: string,
|
||||||
|
propertyName: string,
|
||||||
|
getter: (instance: Instance) -> T,
|
||||||
|
setter: ((instance: Instance, value: T) -> ())?
|
||||||
|
)
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Roblox
|
||||||
|
|
||||||
|
Implements a method for all instances of the given `className`.
|
||||||
|
|
||||||
|
This takes into account class hierarchies, so implementing a method
|
||||||
|
for the `BasePart` class will also implement it for `Part` and others,
|
||||||
|
unless a more specific implementation is added to the `Part` class directly.
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
The given `callback` will be called every time the method is called,
|
||||||
|
and will receive the instance it was called on as its first argument.
|
||||||
|
The remaining arguments will be what the caller passed to the method, and
|
||||||
|
all values returned from the callback will then be returned to the caller.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local roblox = require("@lune/roblox")
|
||||||
|
|
||||||
|
local part = roblox.Instance.new("Part")
|
||||||
|
|
||||||
|
roblox.implementMethod("BasePart", "TestMethod", function(instance, ...)
|
||||||
|
print("Called TestMethod on instance", instance, "with", ...)
|
||||||
|
end)
|
||||||
|
|
||||||
|
part:TestMethod("Hello", "world!")
|
||||||
|
--> Called TestMethod on instance Part with Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
@param className The class to implement the method for.
|
||||||
|
@param methodName The name of the method to implement.
|
||||||
|
@param callback The function which will be called when the method is called.
|
||||||
|
]=]
|
||||||
|
function roblox.implementMethod(
|
||||||
|
className: string,
|
||||||
|
methodName: string,
|
||||||
|
callback: (instance: Instance, ...any) -> ...any
|
||||||
|
)
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Make typedefs for all of the datatypes as well...
|
||||||
|
roblox.Instance = (nil :: any) :: {
|
||||||
|
new: ((className: "DataModel") -> DataModel) & ((className: string) -> Instance),
|
||||||
|
}
|
||||||
|
|
||||||
|
return roblox
|
200
.lune/.lune-defs/serde.luau
Normal file
200
.lune/.lune-defs/serde.luau
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@interface EncodeDecodeFormat
|
||||||
|
|
||||||
|
A serialization/deserialization format supported by the Serde library.
|
||||||
|
|
||||||
|
Currently supported formats:
|
||||||
|
|
||||||
|
| Name | Learn More |
|
||||||
|
|:-------|:---------------------|
|
||||||
|
| `json` | https://www.json.org |
|
||||||
|
| `yaml` | https://yaml.org |
|
||||||
|
| `toml` | https://toml.io |
|
||||||
|
]=]
|
||||||
|
export type EncodeDecodeFormat = "json" | "yaml" | "toml"
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@interface CompressDecompressFormat
|
||||||
|
|
||||||
|
A compression/decompression format supported by the Serde library.
|
||||||
|
|
||||||
|
Currently supported formats:
|
||||||
|
|
||||||
|
| Name | Learn More |
|
||||||
|
|:---------|:----------------------------------|
|
||||||
|
| `brotli` | https://github.com/google/brotli |
|
||||||
|
| `gzip` | https://www.gnu.org/software/gzip |
|
||||||
|
| `lz4` | https://github.com/lz4/lz4 |
|
||||||
|
| `zlib` | https://www.zlib.net |
|
||||||
|
]=]
|
||||||
|
export type CompressDecompressFormat = "brotli" | "gzip" | "lz4" | "zlib"
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@interface HashAlgorithm
|
||||||
|
|
||||||
|
A hash algorithm supported by the Serde library.
|
||||||
|
|
||||||
|
Currently supported algorithms:
|
||||||
|
|
||||||
|
| Name | Learn More |
|
||||||
|
|:-----------|:-------------------------------------|
|
||||||
|
| `md5` | https://en.wikipedia.org/wiki/MD5 |
|
||||||
|
| `sha1` | https://en.wikipedia.org/wiki/SHA-1 |
|
||||||
|
| `sha224` | https://en.wikipedia.org/wiki/SHA-2 |
|
||||||
|
| `sha256` | https://en.wikipedia.org/wiki/SHA-2 |
|
||||||
|
| `sha384` | https://en.wikipedia.org/wiki/SHA-2 |
|
||||||
|
| `sha512` | https://en.wikipedia.org/wiki/SHA-2 |
|
||||||
|
| `sha3-224` | https://en.wikipedia.org/wiki/SHA-3 |
|
||||||
|
| `sha3-256` | https://en.wikipedia.org/wiki/SHA-3 |
|
||||||
|
| `sha3-384` | https://en.wikipedia.org/wiki/SHA-3 |
|
||||||
|
| `sha3-512` | https://en.wikipedia.org/wiki/SHA-3 |
|
||||||
|
| `blake3` | https://en.wikipedia.org/wiki/BLAKE3 |
|
||||||
|
]=]
|
||||||
|
export type HashAlgorithm =
|
||||||
|
"md5"
|
||||||
|
| "sha1"
|
||||||
|
| "sha224"
|
||||||
|
| "sha256"
|
||||||
|
| "sha384"
|
||||||
|
| "sha512"
|
||||||
|
| "sha3-224"
|
||||||
|
| "sha3-256"
|
||||||
|
| "sha3-384"
|
||||||
|
| "sha3-512"
|
||||||
|
| "blake3"
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Serde
|
||||||
|
|
||||||
|
Built-in library for:
|
||||||
|
- serialization & deserialization
|
||||||
|
- encoding & decoding
|
||||||
|
- compression
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local serde = require("@lune/serde")
|
||||||
|
|
||||||
|
-- Parse different file formats into lua tables
|
||||||
|
local someJson = serde.decode("json", fs.readFile("myFile.json"))
|
||||||
|
local someToml = serde.decode("toml", fs.readFile("myFile.toml"))
|
||||||
|
local someYaml = serde.decode("yaml", fs.readFile("myFile.yaml"))
|
||||||
|
|
||||||
|
-- Write lua tables to files in different formats
|
||||||
|
fs.writeFile("myFile.json", serde.encode("json", someJson))
|
||||||
|
fs.writeFile("myFile.toml", serde.encode("toml", someToml))
|
||||||
|
fs.writeFile("myFile.yaml", serde.encode("yaml", someYaml))
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local serde = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Encodes the given value using the given format.
|
||||||
|
|
||||||
|
See [`EncodeDecodeFormat`] for a list of supported formats.
|
||||||
|
|
||||||
|
@param format The format to use
|
||||||
|
@param value The value to encode
|
||||||
|
@param pretty If the encoded string should be human-readable, including things such as newlines and spaces. Only supported for json and toml formats, and defaults to false
|
||||||
|
@return The encoded string
|
||||||
|
]=]
|
||||||
|
function serde.encode(format: EncodeDecodeFormat, value: any, pretty: boolean?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Decodes the given string using the given format into a lua value.
|
||||||
|
|
||||||
|
See [`EncodeDecodeFormat`] for a list of supported formats.
|
||||||
|
|
||||||
|
@param format The format to use
|
||||||
|
@param encoded The string to decode
|
||||||
|
@return The decoded lua value
|
||||||
|
]=]
|
||||||
|
function serde.decode(format: EncodeDecodeFormat, encoded: buffer | string): any
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Compresses the given string using the given format.
|
||||||
|
|
||||||
|
See [`CompressDecompressFormat`] for a list of supported formats.
|
||||||
|
|
||||||
|
@param format The format to use
|
||||||
|
@param s The string to compress
|
||||||
|
@param level The compression level to use, clamped to the format's limits. The best compression level is used by default
|
||||||
|
@return The compressed string
|
||||||
|
]=]
|
||||||
|
function serde.compress(format: CompressDecompressFormat, s: buffer | string, level: number?): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Decompresses the given string using the given format.
|
||||||
|
|
||||||
|
See [`CompressDecompressFormat`] for a list of supported formats.
|
||||||
|
|
||||||
|
@param format The format to use
|
||||||
|
@param s The string to decompress
|
||||||
|
@return The decompressed string
|
||||||
|
]=]
|
||||||
|
function serde.decompress(format: CompressDecompressFormat, s: buffer | string): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Hashes the given message using the given algorithm
|
||||||
|
and returns the hash as a hex string.
|
||||||
|
|
||||||
|
See [`HashAlgorithm`] for a list of supported algorithms.
|
||||||
|
|
||||||
|
@param algorithm The algorithm to use
|
||||||
|
@param message The message to hash
|
||||||
|
@return The hash as a hex string
|
||||||
|
]=]
|
||||||
|
function serde.hash(algorithm: HashAlgorithm, message: string | buffer): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Serde
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Hashes the given message using HMAC with the given secret
|
||||||
|
and algorithm, returning the hash as a base64 string.
|
||||||
|
|
||||||
|
See [`HashAlgorithm`] for a list of supported algorithms.
|
||||||
|
|
||||||
|
@param algorithm The algorithm to use
|
||||||
|
@param message The message to hash
|
||||||
|
@return The hash as a base64 string
|
||||||
|
]=]
|
||||||
|
function serde.hmac(
|
||||||
|
algorithm: HashAlgorithm,
|
||||||
|
message: string | buffer,
|
||||||
|
secret: string | buffer
|
||||||
|
): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return serde
|
161
.lune/.lune-defs/stdio.luau
Normal file
161
.lune/.lune-defs/stdio.luau
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
export type Color =
|
||||||
|
"reset"
|
||||||
|
| "black"
|
||||||
|
| "red"
|
||||||
|
| "green"
|
||||||
|
| "yellow"
|
||||||
|
| "blue"
|
||||||
|
| "purple"
|
||||||
|
| "cyan"
|
||||||
|
| "white"
|
||||||
|
export type Style = "reset" | "bold" | "dim"
|
||||||
|
|
||||||
|
type PromptFn = (
|
||||||
|
(() -> string)
|
||||||
|
& ((kind: "text", message: string?, defaultOrOptions: string?) -> string)
|
||||||
|
& ((kind: "confirm", message: string, defaultOrOptions: boolean?) -> boolean)
|
||||||
|
& ((kind: "select", message: string?, defaultOrOptions: { string }) -> number?)
|
||||||
|
& ((kind: "multiselect", message: string?, defaultOrOptions: { string }) -> { number }?)
|
||||||
|
)
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
@function prompt
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Prompts for user input using the wanted kind of prompt:
|
||||||
|
|
||||||
|
* `"text"` - Prompts for a plain text string from the user
|
||||||
|
* `"confirm"` - Prompts the user to confirm with y / n (yes / no)
|
||||||
|
* `"select"` - Prompts the user to select *one* value from a list
|
||||||
|
* `"multiselect"` - Prompts the user to select *one or more* values from a list
|
||||||
|
* `nil` - Equivalent to `"text"` with no extra arguments
|
||||||
|
|
||||||
|
@param kind The kind of prompt to use
|
||||||
|
@param message The message to show the user
|
||||||
|
@param defaultOrOptions The default value for the prompt, or options to choose from for selection prompts
|
||||||
|
]=]
|
||||||
|
local prompt: PromptFn = function(kind: any, message: any, defaultOrOptions: any)
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Stdio
|
||||||
|
|
||||||
|
Built-in standard input / output & utility functions
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
|
||||||
|
-- Prompting the user for basic input
|
||||||
|
local text: string = stdio.prompt("text", "Please write some text")
|
||||||
|
local confirmed: boolean = stdio.prompt("confirm", "Please confirm this action")
|
||||||
|
|
||||||
|
-- Writing directly to stdout or stderr, without the auto-formatting of print/warn/error
|
||||||
|
stdio.write("Hello, ")
|
||||||
|
stdio.write("World! ")
|
||||||
|
stdio.write("All on the same line")
|
||||||
|
stdio.ewrite("\nAnd some error text, too")
|
||||||
|
|
||||||
|
-- Reading the entire input from stdin
|
||||||
|
local input = stdio.readToEnd()
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local stdio = {}
|
||||||
|
|
||||||
|
stdio.prompt = prompt
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Return an ANSI string that can be used to modify the persistent output color.
|
||||||
|
|
||||||
|
Pass `"reset"` to get a string that can reset the persistent output color.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
stdio.write(stdio.color("red"))
|
||||||
|
print("This text will be red")
|
||||||
|
stdio.write(stdio.color("reset"))
|
||||||
|
print("This text will be normal")
|
||||||
|
```
|
||||||
|
|
||||||
|
@param color The color to use
|
||||||
|
@return A printable ANSI string
|
||||||
|
]=]
|
||||||
|
function stdio.color(color: Color): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Return an ANSI string that can be used to modify the persistent output style.
|
||||||
|
|
||||||
|
Pass `"reset"` to get a string that can reset the persistent output style.
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
stdio.write(stdio.style("bold"))
|
||||||
|
print("This text will be bold")
|
||||||
|
stdio.write(stdio.style("reset"))
|
||||||
|
print("This text will be normal")
|
||||||
|
```
|
||||||
|
|
||||||
|
@param style The style to use
|
||||||
|
@return A printable ANSI string
|
||||||
|
]=]
|
||||||
|
function stdio.style(style: Style): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Formats arguments into a human-readable string with syntax highlighting for tables.
|
||||||
|
|
||||||
|
@param ... The values to format
|
||||||
|
@return The formatted string
|
||||||
|
]=]
|
||||||
|
function stdio.format(...: any): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
|
||||||
|
Writes a string directly to stdout, without any newline.
|
||||||
|
|
||||||
|
@param s The string to write to stdout
|
||||||
|
]=]
|
||||||
|
function stdio.write(s: string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
|
||||||
|
Writes a string directly to stderr, without any newline.
|
||||||
|
|
||||||
|
@param s The string to write to stderr
|
||||||
|
]=]
|
||||||
|
function stdio.ewrite(s: string) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Stdio
|
||||||
|
@tag must_use
|
||||||
|
|
||||||
|
Reads the entire input from stdin.
|
||||||
|
|
||||||
|
@return The input from stdin
|
||||||
|
]=]
|
||||||
|
function stdio.readToEnd(): string
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return stdio
|
99
.lune/.lune-defs/task.luau
Normal file
99
.lune/.lune-defs/task.luau
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
--[=[
|
||||||
|
@class Task
|
||||||
|
|
||||||
|
Built-in task scheduler & thread spawning
|
||||||
|
|
||||||
|
### Example usage
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local task = require("@lune/task")
|
||||||
|
|
||||||
|
-- Waiting for a certain amount of time
|
||||||
|
task.wait(1)
|
||||||
|
print("Waited for one second")
|
||||||
|
|
||||||
|
-- Running a task after a given amount of time
|
||||||
|
task.delay(2, function()
|
||||||
|
print("Ran after two seconds")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Spawning a new task that runs concurrently
|
||||||
|
task.spawn(function()
|
||||||
|
print("Running instantly")
|
||||||
|
task.wait(1)
|
||||||
|
print("One second passed inside the task")
|
||||||
|
end)
|
||||||
|
|
||||||
|
print("Running after task.spawn yields")
|
||||||
|
```
|
||||||
|
]=]
|
||||||
|
local task = {}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Task
|
||||||
|
|
||||||
|
Stops a currently scheduled thread from resuming.
|
||||||
|
|
||||||
|
@param thread The thread to cancel
|
||||||
|
]=]
|
||||||
|
function task.cancel(thread: thread) end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Task
|
||||||
|
|
||||||
|
Defers a thread or function to run at the end of the current task queue.
|
||||||
|
|
||||||
|
@param functionOrThread The function or thread to defer
|
||||||
|
@return The thread that will be deferred
|
||||||
|
]=]
|
||||||
|
function task.defer<T...>(functionOrThread: thread | (T...) -> ...any, ...: T...): thread
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Task
|
||||||
|
|
||||||
|
Delays a thread or function to run after `duration` seconds.
|
||||||
|
|
||||||
|
@param functionOrThread The function or thread to delay
|
||||||
|
@return The thread that will be delayed
|
||||||
|
]=]
|
||||||
|
function task.delay<T...>(
|
||||||
|
duration: number,
|
||||||
|
functionOrThread: thread | (T...) -> ...any,
|
||||||
|
...: T...
|
||||||
|
): thread
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Task
|
||||||
|
|
||||||
|
Instantly runs a thread or function.
|
||||||
|
|
||||||
|
If the spawned task yields, the thread that spawned the task
|
||||||
|
will resume, letting the spawned task run in the background.
|
||||||
|
|
||||||
|
@param functionOrThread The function or thread to spawn
|
||||||
|
@return The thread that was spawned
|
||||||
|
]=]
|
||||||
|
function task.spawn<T...>(functionOrThread: thread | (T...) -> ...any, ...: T...): thread
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Task
|
||||||
|
|
||||||
|
Waits for *at least* the given amount of time.
|
||||||
|
|
||||||
|
The minimum wait time possible when using `task.wait` is limited by the underlying OS sleep implementation.
|
||||||
|
For most systems this means `task.wait` is accurate down to about 5 milliseconds or less.
|
||||||
|
|
||||||
|
@param duration The amount of time to wait
|
||||||
|
@return The exact amount of time waited
|
||||||
|
]=]
|
||||||
|
function task.wait(duration: number?): number
|
||||||
|
return nil :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
return task
|
11
.lune/analyze.luau
Normal file
11
.lune/analyze.luau
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
--!strict
|
||||||
|
local spawn = require("util/spawn")
|
||||||
|
|
||||||
|
spawn.start("lune run install-packages")
|
||||||
|
spawn.start("rojo sourcemap default.project.json -o sourcemap.json")
|
||||||
|
spawn.start(
|
||||||
|
"curl https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/refs/heads/main/scripts/globalTypes.RobloxScriptSecurity.d.luau -o globalTypes.d.luau"
|
||||||
|
)
|
||||||
|
spawn.start(
|
||||||
|
"luau-lsp analyze --definitions=globalTypes.d.luau --base-luaurc=.luaurc --sourcemap=sourcemap.json --settings=luau_lsp_settings.json --no-strict-dm-types --ignore **/_Index/** --ignore **/.pesde/** --ignore src/client.luau --ignore src/server.luau --ignore src/types.luau src/"
|
||||||
|
)
|
7
.lune/build.luau
Normal file
7
.lune/build.luau
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
--!strict
|
||||||
|
local spawn = require("util/spawn")
|
||||||
|
|
||||||
|
spawn.start("lune run install-packages")
|
||||||
|
spawn.start("rojo sourcemap default.project.json -o sourcemap.json")
|
||||||
|
spawn.start("darklua process --config .darklua.json src/ dist/", { env = { ROBLOX_DEV = "false" } })
|
||||||
|
spawn.start("rojo build build.project.json -o build.rbxm")
|
6
.lune/check.luau
Normal file
6
.lune/check.luau
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--!strict
|
||||||
|
local spawn = require("util/spawn")
|
||||||
|
|
||||||
|
spawn.start("lune run analyze")
|
||||||
|
spawn.start("stylua src/")
|
||||||
|
spawn.start("selene src/")
|
58
.lune/dev.luau
Normal file
58
.lune/dev.luau
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
--!strict
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
local task = require("@lune/task")
|
||||||
|
|
||||||
|
local spawn = require("util/spawn")
|
||||||
|
local watch = require("util/watch")
|
||||||
|
|
||||||
|
task.spawn(watch, "pesde.toml", function()
|
||||||
|
spawn.spawn("lune run install-packages")
|
||||||
|
end, false)
|
||||||
|
|
||||||
|
spawn.spawn("rojo sourcemap default.project.json -o sourcemap.json --watch")
|
||||||
|
spawn.spawn("darklua process --config .darklua.json --watch src/ dist/", { env = { ROBLOX_DEV = "true" } })
|
||||||
|
spawn.spawn("blink net.blink --watch")
|
||||||
|
spawn.spawn("rojo serve build.project.json")
|
||||||
|
|
||||||
|
task.wait(2.5)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local start_commit = stdio.prompt("confirm", "Start commit? -- `y` to start a commit, `n` to exit the script")
|
||||||
|
if not start_commit then
|
||||||
|
process.exit(0)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, check_result = pcall(spawn.start, "lune run check")
|
||||||
|
if not check_result.ok then
|
||||||
|
warn("Check didn't go ok, aborting commit")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local commit_title = stdio.prompt("text", "Commit title -- leave blank to stop committing")
|
||||||
|
if not commit_title or commit_title == "" then
|
||||||
|
print("Stopping commit")
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
local commit_messages = { `-m`, commit_title }
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local commit_message = stdio.prompt("text", "Commit message -- added to the description, leave blank to finish")
|
||||||
|
if not commit_message or commit_message == "" then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(commit_messages, "-m")
|
||||||
|
table.insert(commit_messages, commit_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
local confirm = stdio.prompt("confirm", "Confirm?")
|
||||||
|
if not confirm then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
spawn.start("git add .")
|
||||||
|
process.spawn("git", { "commit", unpack(commit_messages) }, { stdio = "forward" })
|
||||||
|
spawn.start("git push")
|
||||||
|
end
|
8
.lune/install-packages.luau
Normal file
8
.lune/install-packages.luau
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
--!strict
|
||||||
|
local spawn = require("util/spawn")
|
||||||
|
|
||||||
|
--spawn.start("wally install")
|
||||||
|
--spawn.start("rojo sourcemap dev-default.project.json -o sourcemap.json")
|
||||||
|
--spawn.start("wally-package-types --sourcemap sourcemap.json Packages/")
|
||||||
|
--spawn.start("wally-package-types --sourcemap sourcemap.json DevPackages/")
|
||||||
|
spawn.start("pesde install")
|
39
.lune/util/spawn.luau
Normal file
39
.lune/util/spawn.luau
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
--!strict
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local task = require("@lune/task")
|
||||||
|
|
||||||
|
--- Start a process with the given command and options
|
||||||
|
--- ```luau
|
||||||
|
--- spawn.start("lune run test")
|
||||||
|
--- ```
|
||||||
|
--- @param cmd string
|
||||||
|
--- @param options process.SpawnOptions?
|
||||||
|
--- @return process.SpawnResult
|
||||||
|
local function start_process(cmd: string, options: process.SpawnOptions?): process.SpawnResult
|
||||||
|
local arguments = string.split(cmd, " ")
|
||||||
|
local command = arguments[1]
|
||||||
|
table.remove(arguments, 1)
|
||||||
|
|
||||||
|
local opts: process.SpawnOptions = options ~= nil and options or {}
|
||||||
|
opts.stdio = opts.stdio ~= nil and opts.stdio or "forward"
|
||||||
|
|
||||||
|
return process.spawn(command, arguments, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- `task.spawn` a process with the given command and options
|
||||||
|
--- ```luau
|
||||||
|
--- spawn.spawn("lune run test") -- process now runs in the background!
|
||||||
|
--- ```
|
||||||
|
--- @param cmd string
|
||||||
|
--- @param options process.SpawnOptions?
|
||||||
|
--- @return process.SpawnResult
|
||||||
|
local function spawn_process(cmd: string, options: process.SpawnOptions?)
|
||||||
|
task.spawn(start_process, cmd, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
local spawn = {
|
||||||
|
start = start_process,
|
||||||
|
spawn = spawn_process,
|
||||||
|
}
|
||||||
|
|
||||||
|
return spawn
|
46
.lune/util/watch.luau
Normal file
46
.lune/util/watch.luau
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
--!strict
|
||||||
|
local fs = require("@lune/fs")
|
||||||
|
local task = require("@lune/task")
|
||||||
|
|
||||||
|
local function watch(path: string, on_change: (path: string, contents: string) -> (), run_initially: boolean?)
|
||||||
|
local initial_metadata = fs.metadata(path)
|
||||||
|
if not initial_metadata.exists then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if run_initially then
|
||||||
|
local initial_contents = fs.readFile(path)
|
||||||
|
|
||||||
|
local initial_success, why = pcall(on_change :: any, path, initial_contents) -- :: any because otherwise it shits itself and the type doesn't give (boolean, string)??
|
||||||
|
if not initial_success then
|
||||||
|
warn(`There was an error while trying to start the watcher thread:\n{why}`)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local last_modification = initial_metadata.modifiedAt
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local metadata = fs.metadata(path)
|
||||||
|
|
||||||
|
if not metadata.exists then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if metadata.modifiedAt == last_modification then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
last_modification = metadata.modifiedAt
|
||||||
|
|
||||||
|
local contents = fs.readFile(path)
|
||||||
|
|
||||||
|
local success, err = pcall(on_change :: any, path, contents)
|
||||||
|
if not success then
|
||||||
|
warn(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
task.wait(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return watch
|
4
.pesde/roblox_sync_config_generator.luau
Normal file
4
.pesde/roblox_sync_config_generator.luau
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local home_dir = if process.os == "windows" then process.env.userprofile else process.env.HOME
|
||||||
|
|
||||||
|
require(home_dir .. "/.pesde/scripts/lune/rojo/roblox_sync_config_generator.luau")
|
4
.pesde/sourcemap_generator.luau
Normal file
4
.pesde/sourcemap_generator.luau
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
local process = require("@lune/process")
|
||||||
|
local home_dir = if process.os == "windows" then process.env.userprofile else process.env.HOME
|
||||||
|
|
||||||
|
require(home_dir .. "/.pesde/scripts/lune/rojo/sourcemap_generator.luau")
|
3
.styluaignore
Normal file
3
.styluaignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
src/net/server.luau
|
||||||
|
src/net/client.luau
|
||||||
|
src/net/types.luau
|
62
.zed/settings.json
Normal file
62
.zed/settings.json
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"lsp": {
|
||||||
|
"luau-lsp": {
|
||||||
|
"settings": {
|
||||||
|
"luau-lsp": {
|
||||||
|
"completion": {
|
||||||
|
"imports": {
|
||||||
|
"enabled": true,
|
||||||
|
"separateGroupsWithLine": true,
|
||||||
|
"suggestServices": true,
|
||||||
|
"suggestRequires": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"mode": "relativeToFile",
|
||||||
|
"directoryAliases": {
|
||||||
|
"@lune/": ".lune/.lune-defs/",
|
||||||
|
"@pkg/": "roblox_packages/",
|
||||||
|
"@net/": "src/net/",
|
||||||
|
"@server/": "src/server/",
|
||||||
|
"@services/": "src/server/services/",
|
||||||
|
"@srv_systems/": "src/server/services/systems/",
|
||||||
|
"@client/": "src/client/",
|
||||||
|
"@controllers/": "src/client/controllers/",
|
||||||
|
"@clt_systems/": "src/client/controllers/systems/",
|
||||||
|
"@shared/": "src/shared/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inlayHints": {
|
||||||
|
"parameterNames": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ext": {
|
||||||
|
"roblox": {
|
||||||
|
"enabled": true,
|
||||||
|
"security_level": "roblox_script"
|
||||||
|
},
|
||||||
|
"fflags": {
|
||||||
|
"enable_by_default": false,
|
||||||
|
"sync": true
|
||||||
|
},
|
||||||
|
"binary": {
|
||||||
|
"ignore_system_version": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"Luau": {
|
||||||
|
"formatter": {
|
||||||
|
"external": {
|
||||||
|
"command": "stylua",
|
||||||
|
"arguments": ["-"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"file_types": {
|
||||||
|
"Luau": ["lua"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
# color-pads
|
# roblox-project-template
|
||||||
|
|
||||||
This is a rewrite of Color Pads using jecs instead of ECR.
|
A template for Roblox projects using rojo, darklua, jecs and sapphire.
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
These workflows are made for [my forgejo instance](https://git.devmarked.win/). In order to make them work on GitHub, you have to change the used actions (such as `marked/setup-rokit`) to match the ones available on GitHub (for example `CompeyDev/setup-rokit`).\
|
||||||
|
You also have to rename `.forgejo` to `.github`, although that should be pretty self explanatory.
|
||||||
|
|
27
build.project.json
Normal file
27
build.project.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "roblox-project-template",
|
||||||
|
"emitLegacyScripts": false,
|
||||||
|
"tree": {
|
||||||
|
"$className": "DataModel",
|
||||||
|
|
||||||
|
"ServerScriptService": {
|
||||||
|
"$path": "dist/server"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ReplicatedStorage": {
|
||||||
|
"$path": "dist/shared",
|
||||||
|
|
||||||
|
"net": {
|
||||||
|
"$path": "dist/net"
|
||||||
|
},
|
||||||
|
|
||||||
|
"client": {
|
||||||
|
"$path": "dist/client"
|
||||||
|
},
|
||||||
|
|
||||||
|
"packages": {
|
||||||
|
"$path": "roblox_packages"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
default.project.json
Normal file
27
default.project.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "roblox-project-template",
|
||||||
|
"emitLegacyScripts": false,
|
||||||
|
"tree": {
|
||||||
|
"$className": "DataModel",
|
||||||
|
|
||||||
|
"ServerScriptService": {
|
||||||
|
"$path": "src/server"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ReplicatedStorage": {
|
||||||
|
"$path": "src/shared",
|
||||||
|
|
||||||
|
"net": {
|
||||||
|
"$path": "src/net"
|
||||||
|
},
|
||||||
|
|
||||||
|
"client": {
|
||||||
|
"$path": "src/client"
|
||||||
|
},
|
||||||
|
|
||||||
|
"packages": {
|
||||||
|
"$path": "roblox_packages"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
luau_lsp_settings.json
Normal file
17
luau_lsp_settings.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"luau-lsp.platform.type": "roblox",
|
||||||
|
"luau-lsp.sourcemap.rojoProjectFile": "default.project.json",
|
||||||
|
"luau-lsp.require.mode": "relativeToFile",
|
||||||
|
"luau-lsp.require.directoryAliases": {
|
||||||
|
"@lune/": ".lune/.lune-defs/",
|
||||||
|
"@pkg/": "roblox_packages/",
|
||||||
|
"@net/": "src/net/",
|
||||||
|
"@server/": "src/server/",
|
||||||
|
"@services/": "src/server/services/",
|
||||||
|
"@srv_systems/": "src/server/services/systems/",
|
||||||
|
"@client/": "src/client/",
|
||||||
|
"@controllers/": "src/client/controllers/",
|
||||||
|
"@clt_systems/": "src/client/controllers/systems/",
|
||||||
|
"@shared/": "src/shared/"
|
||||||
|
}
|
||||||
|
}
|
11
net.blink
Normal file
11
net.blink
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
option Casing = Snake
|
||||||
|
option ServerOutput = "src/net/server.luau"
|
||||||
|
option ClientOutput = "src/net/client.luau"
|
||||||
|
option TypesOutput = "src/net/types.luau"
|
||||||
|
|
||||||
|
event replicate_data {
|
||||||
|
from: Server,
|
||||||
|
type: Reliable,
|
||||||
|
call: SingleSync,
|
||||||
|
data: buffer,
|
||||||
|
}
|
335
pesde.lock
Normal file
335
pesde.lock
Normal file
|
@ -0,0 +1,335 @@
|
||||||
|
name = "marked/roblox_project_template"
|
||||||
|
version = "0.1.0"
|
||||||
|
target = "roblox"
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/jabby"."0.2.0-rc.3 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/jabby"."0.2.0-rc.3 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/jabby"."0.2.0-rc.3 roblox".dependencies]
|
||||||
|
"wally#alicesaidhi/pebble" = ["0.1.0-rc.7 roblox", "pebble"]
|
||||||
|
"wally#centau/vide" = ["0.3.1 roblox", "vide"]
|
||||||
|
"wally#corecii/greentea" = ["0.4.10 roblox", "t"]
|
||||||
|
"wally#ukendio/jecs" = ["0.3.2 roblox", "jecs"]
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/jabby"."0.2.0-rc.3 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#alicesaidhi/jabby"
|
||||||
|
version = "0.2.0-rc.3"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/jabby"."0.2.0-rc.3 roblox".pkg_ref.dependencies]
|
||||||
|
jecs = [{ wally = "wally#ukendio/jecs", version = ">=0.3.0-rc.2, <0.4.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
pebble = [{ wally = "wally#alicesaidhi/pebble", version = ">=0.1.0-rc.7, <0.2.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
t = [{ wally = "wally#corecii/greentea", version = ">=0.4.7, <0.5.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
vide = [{ wally = "wally#centau/vide", version = ">=0.3.1, <0.4.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/pebble"."0.1.0-rc.7 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/pebble"."0.1.0-rc.7 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/pebble"."0.1.0-rc.7 roblox".dependencies]
|
||||||
|
"wally#centau/vide" = ["0.3.1 roblox", "vide"]
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/pebble"."0.1.0-rc.7 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#alicesaidhi/pebble"
|
||||||
|
version = "0.1.0-rc.7"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#alicesaidhi/pebble"."0.1.0-rc.7 roblox".pkg_ref.dependencies]
|
||||||
|
vide = [{ wally = "wally#centau/vide", version = ">=0.3.1, <0.4.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#centau/vide"."0.3.1 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#centau/vide"."0.3.1 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#centau/vide"."0.3.1 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#centau/vide"
|
||||||
|
version = "0.3.1"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#corecii/greentea"."0.4.10 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#corecii/greentea"."0.4.10 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#corecii/greentea"."0.4.10 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#corecii/greentea"
|
||||||
|
version = "0.4.10"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#corecii/greentea"."0.4.10 roblox".pkg_ref.dependencies]
|
||||||
|
Jest = [{ wally = "wally#jsdotlua/jest", version = ">=3.6.1-rc.2, <4.0.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
JestGlobals = [{ wally = "wally#jsdotlua/jest-globals", version = ">=3.6.1-rc.2, <4.0.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
RegExp = [{ wally = "wally#jsdotlua/luau-regexp", version = ">=0.2.1, <0.3.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
_jest-roblox-shared = [{ wally = "wally#jsdotlua/jest-roblox-shared", version = ">=3.6.1-rc.2, <4.0.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/keyform"."0.2.2 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/keyform"."0.2.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/keyform"."0.2.2 roblox".dependencies]
|
||||||
|
"wally#ffrostflame/luausignal" = ["0.1.3 roblox", "Signal"]
|
||||||
|
"wally#ffrostflame/tablekit" = ["0.2.4 roblox", "TableKit"]
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/keyform"."0.2.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#ffrostflame/keyform"
|
||||||
|
version = "0.2.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/keyform"."0.2.2 roblox".pkg_ref.dependencies]
|
||||||
|
Signal = [{ wally = "wally#ffrostflame/luausignal", version = ">=0.1.3, <0.2.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
TableKit = [{ wally = "wally#ffrostflame/tablekit", version = ">=0.2.2, <0.3.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/luausignal"."0.1.3 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/luausignal"."0.1.3 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/luausignal"."0.1.3 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#ffrostflame/luausignal"
|
||||||
|
version = "0.1.3"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/tablekit"."0.2.4 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/tablekit"."0.2.4 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#ffrostflame/tablekit"."0.2.4 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#ffrostflame/tablekit"
|
||||||
|
version = "0.2.4"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/jecs-utils"."0.1.5 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/jecs-utils"."0.1.5 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'dist\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/jecs-utils"."0.1.5 roblox".dependencies]
|
||||||
|
"wally#ukendio/jecs" = ["0.3.2 roblox", "jecs"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/jecs-utils"."0.1.5 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/jecs-utils"
|
||||||
|
version = "0.1.5"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/jecs-utils"."0.1.5 roblox".pkg_ref.dependencies]
|
||||||
|
jecs = [{ wally = "wally#ukendio/jecs", version = ">=0.3.2, <0.4.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire"."0.1.3 roblox"]
|
||||||
|
direct = ["sapphire", { wally = "wally#mark-marks/sapphire", version = "^0.1.3" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire"."0.1.3 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'lib\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire"."0.1.3 roblox".dependencies]
|
||||||
|
"wally#red-blox/signal" = ["2.0.2 roblox", "signal"]
|
||||||
|
"wally#red-blox/spawn" = ["1.1.0 roblox", "spawn"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire"."0.1.3 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/sapphire"
|
||||||
|
version = "0.1.3"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire"."0.1.3 roblox".pkg_ref.dependencies]
|
||||||
|
signal = [{ wally = "wally#red-blox/signal", version = ">=2.0.2, <3.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
spawn = [{ wally = "wally#red-blox/spawn", version = ">=1.1.0, <2.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-data"."0.1.2 roblox"]
|
||||||
|
direct = ["sapphire_data", { wally = "wally#mark-marks/sapphire-data", version = "^0.1.2" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-data"."0.1.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'lib\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-data"."0.1.2 roblox".dependencies]
|
||||||
|
"wally#ffrostflame/keyform" = ["0.2.2 roblox", "keyform"]
|
||||||
|
"wally#nezuo/delta-compress" = ["0.1.3 roblox", "delta_compress"]
|
||||||
|
"wally#red-blox/signal" = ["2.0.2 roblox", "signal"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-data"."0.1.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/sapphire-data"
|
||||||
|
version = "0.1.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-data"."0.1.2 roblox".pkg_ref.dependencies]
|
||||||
|
delta_compress = [{ wally = "wally#nezuo/delta-compress", version = ">=0.1.3, <0.2.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
keyform = [{ wally = "wally#ffrostflame/keyform", version = ">=0.2.2, <0.3.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
signal = [{ wally = "wally#red-blox/signal", version = ">=2.0.2, <3.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-jecs"."0.2.0-rc.2 roblox"]
|
||||||
|
direct = ["sapphire_jecs", { wally = "wally#mark-marks/sapphire-jecs", version = "^0.2.0-rc.2" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-jecs"."0.2.0-rc.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'lib\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-jecs"."0.2.0-rc.2 roblox".dependencies]
|
||||||
|
"wally#alicesaidhi/jabby" = ["0.2.0-rc.3 roblox", "jabby"]
|
||||||
|
"wally#mark-marks/jecs-utils" = ["0.1.5 roblox", "jecs_utils"]
|
||||||
|
"wally#ukendio/jecs" = ["0.3.2 roblox", "jecs"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-jecs"."0.2.0-rc.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/sapphire-jecs"
|
||||||
|
version = "0.2.0-rc.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-jecs"."0.2.0-rc.2 roblox".pkg_ref.dependencies]
|
||||||
|
jabby = [{ wally = "wally#alicesaidhi/jabby", version = ">=0.2.0-rc.2, <0.3.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
jecs = [{ wally = "wally#ukendio/jecs", version = ">=0.3.2, <0.4.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
jecs_utils = [{ wally = "wally#mark-marks/jecs-utils", version = ">=0.1.5, <0.2.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-lifecycles"."0.1.2 roblox"]
|
||||||
|
direct = ["sapphire_lifecycles", { wally = "wally#mark-marks/sapphire-lifecycles", version = "^0.1.2" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-lifecycles"."0.1.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'lib\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-lifecycles"."0.1.2 roblox".dependencies]
|
||||||
|
"wally#red-blox/signal" = ["2.0.2 roblox", "signal"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-lifecycles"."0.1.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/sapphire-lifecycles"
|
||||||
|
version = "0.1.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-lifecycles"."0.1.2 roblox".pkg_ref.dependencies]
|
||||||
|
signal = [{ wally = "wally#red-blox/signal", version = ">=2.0.2, <3.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-logging"."0.1.2 roblox"]
|
||||||
|
direct = ["sapphire_logging", { wally = "wally#mark-marks/sapphire-logging", version = "^0.1.2" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-logging"."0.1.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'lib\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-logging"."0.1.2 roblox".dependencies]
|
||||||
|
"wally#red-blox/signal" = ["2.0.2 roblox", "signal"]
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-logging"."0.1.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#mark-marks/sapphire-logging"
|
||||||
|
version = "0.1.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#mark-marks/sapphire-logging"."0.1.2 roblox".pkg_ref.dependencies]
|
||||||
|
signal = [{ wally = "wally#red-blox/signal", version = ">=2.0.2, <3.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#nezuo/delta-compress"."0.1.3 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#nezuo/delta-compress"."0.1.3 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.lua'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#nezuo/delta-compress"."0.1.3 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#nezuo/delta-compress"
|
||||||
|
version = "0.1.3"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#nezuo/delta-compress"."0.1.3 roblox".pkg_ref.dependencies]
|
||||||
|
Midori = [{ wally = "wally#nezuo/midori", version = ">=0.1.3, <0.2.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
RemotePacketSizeCounter = [{ wally = "wally#pysephwasntavailable/remotepacketsizecounter", version = ">=2.4.1, <3.0.0", index = "https://github.com/UpliftGames/wally-index" }, "dev"]
|
||||||
|
|
||||||
|
[graph."wally#red-blox/signal"."2.0.2 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#red-blox/signal"."2.0.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = "Signal.luau"
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#red-blox/signal"."2.0.2 roblox".dependencies]
|
||||||
|
"wally#red-blox/spawn" = ["1.1.0 roblox", "Spawn"]
|
||||||
|
|
||||||
|
[graph."wally#red-blox/signal"."2.0.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#red-blox/signal"
|
||||||
|
version = "2.0.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#red-blox/signal"."2.0.2 roblox".pkg_ref.dependencies]
|
||||||
|
Spawn = [{ wally = "wally#red-blox/spawn", version = ">=1.0.0, <2.0.0", index = "https://github.com/UpliftGames/wally-index" }, "standard"]
|
||||||
|
|
||||||
|
[graph."wally#red-blox/spawn"."1.1.0 roblox"]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#red-blox/spawn"."1.1.0 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = "Spawn.luau"
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#red-blox/spawn"."1.1.0 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#red-blox/spawn"
|
||||||
|
version = "1.1.0"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[graph."wally#ukendio/jecs"."0.3.2 roblox"]
|
||||||
|
direct = ["jecs", { wally = "wally#ukendio/jecs", version = "^0.3.2" }]
|
||||||
|
ty = "standard"
|
||||||
|
|
||||||
|
[graph."wally#ukendio/jecs"."0.3.2 roblox".target]
|
||||||
|
environment = "roblox"
|
||||||
|
lib = 'src\init.luau'
|
||||||
|
build_files = []
|
||||||
|
|
||||||
|
[graph."wally#ukendio/jecs"."0.3.2 roblox".pkg_ref]
|
||||||
|
ref_ty = "wally"
|
||||||
|
wally = "wally#ukendio/jecs"
|
||||||
|
version = "0.3.2"
|
||||||
|
index_url = "https://github.com/UpliftGames/wally-index"
|
26
pesde.toml
Normal file
26
pesde.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name = "marked/roblox_project_template"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Template for Roblox projects"
|
||||||
|
authors = ["marked"]
|
||||||
|
repository = "https://git.devmarked.win/marked/roblox-project-template"
|
||||||
|
|
||||||
|
[target]
|
||||||
|
environment = "roblox"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
roblox_sync_config_generator = ".pesde/roblox_sync_config_generator.luau"
|
||||||
|
sourcemap_generator = ".pesde/sourcemap_generator.luau"
|
||||||
|
|
||||||
|
[indices]
|
||||||
|
default = "https://github.com/daimond113/pesde-index"
|
||||||
|
|
||||||
|
[wally_indices]
|
||||||
|
default = "https://github.com/UpliftGames/wally-index"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sapphire = { wally = "wally#mark-marks/sapphire", version = "^0.1.3" }
|
||||||
|
sapphire_data = { wally = "wally#mark-marks/sapphire-data", version = "^0.1.2" }
|
||||||
|
sapphire_jecs = { wally = "wally#mark-marks/sapphire-jecs", version = "0.2.0-rc.2" }
|
||||||
|
sapphire_logging = { wally = "wally#mark-marks/sapphire-logging", version = "^0.1.2" }
|
||||||
|
sapphire_lifecycles = { wally = "wally#mark-marks/sapphire-lifecycles", version = "^0.1.2" }
|
||||||
|
jecs = { wally = "wally#ukendio/jecs", version = "^0.3.2" }
|
14
rokit.toml
Normal file
14
rokit.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# This file lists tools managed by Rokit, a toolchain manager for Roblox projects.
|
||||||
|
# For more information, see https://github.com/rojo-rbx/rokit
|
||||||
|
|
||||||
|
# New tools can be added by running `rokit add <tool>` in a terminal.
|
||||||
|
|
||||||
|
[tools]
|
||||||
|
pesde = "daimond113/pesde@0.5.0-rc.7"
|
||||||
|
stylua = "johnnymorganz/stylua@0.20.0"
|
||||||
|
lune = "lune-org/lune@0.8.9"
|
||||||
|
luau-lsp = "johnnymorganz/luau-lsp@1.35.0"
|
||||||
|
selene = "kampfkarren/selene@0.27.1"
|
||||||
|
darklua = "seaofvoices/darklua@0.14.0"
|
||||||
|
blink = "1axen/blink@0.14.15"
|
||||||
|
rojo = "rojo-rbx/rojo@7.4.4"
|
2
selene.toml
Normal file
2
selene.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
std = "selene_definitions"
|
||||||
|
exclude = ["src/net/client.luau", "src/net/server.luau", "src/net/types.luau"]
|
6
selene_definitions.yaml
Normal file
6
selene_definitions.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
base: roblox
|
||||||
|
name: selene_definitions
|
||||||
|
globals:
|
||||||
|
require:
|
||||||
|
args:
|
||||||
|
- type: string
|
15
src/client/controllers/receive_data.luau
Normal file
15
src/client/controllers/receive_data.luau
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
--!strict
|
||||||
|
local data = require("@client/data")
|
||||||
|
local net = require("@net/client")
|
||||||
|
|
||||||
|
local function init()
|
||||||
|
net.replicate_data.on(function(difference)
|
||||||
|
data.apply_difference(difference)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
init = init,
|
||||||
|
-- data is important
|
||||||
|
priority = math.huge,
|
||||||
|
}
|
0
src/client/controllers/systems/.gitkeep
Normal file
0
src/client/controllers/systems/.gitkeep
Normal file
14
src/client/data.luau
Normal file
14
src/client/data.luau
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--!strict
|
||||||
|
local sapphire_data = require("@pkg/sapphire_data")
|
||||||
|
|
||||||
|
export type data = {
|
||||||
|
coins: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
local template: data = {
|
||||||
|
coins = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sapphire_data.client({
|
||||||
|
template = template,
|
||||||
|
})
|
16
src/client/main.client.luau
Normal file
16
src/client/main.client.luau
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
--!strict
|
||||||
|
local sapphire = require("@pkg/sapphire")
|
||||||
|
|
||||||
|
local data = require("@client/data")
|
||||||
|
local ecs = require("@pkg/sapphire_jecs")
|
||||||
|
local lifecycles = require("@pkg/sapphire_lifecycles")
|
||||||
|
|
||||||
|
local controllers = script.Parent.controllers
|
||||||
|
|
||||||
|
sapphire()
|
||||||
|
:register_singletons(controllers)
|
||||||
|
:register_singletons(controllers.systems)
|
||||||
|
:use(data)
|
||||||
|
:use(ecs)
|
||||||
|
:use(lifecycles)
|
||||||
|
:start()
|
10
src/client/player.luau
Normal file
10
src/client/player.luau
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--!strict
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
local components = require("@shared/components")
|
||||||
|
local ecs = require("@pkg/sapphire_jecs")
|
||||||
|
|
||||||
|
local player = Players.LocalPlayer
|
||||||
|
local player_entity = ecs.ref("local-player"):set(components.player, player):id()
|
||||||
|
|
||||||
|
return player_entity
|
265
src/net/client.luau
Normal file
265
src/net/client.luau
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
--!strict
|
||||||
|
--!native
|
||||||
|
--!optimize 2
|
||||||
|
--!nolint LocalShadow
|
||||||
|
--#selene: allow(shadowing)
|
||||||
|
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
|
||||||
|
-- This file is not meant to be edited
|
||||||
|
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local Invocations = 0
|
||||||
|
|
||||||
|
local SendSize = 64
|
||||||
|
local SendOffset = 0
|
||||||
|
local SendCursor = 0
|
||||||
|
local SendBuffer = buffer.create(64)
|
||||||
|
local SendInstances = {}
|
||||||
|
|
||||||
|
local RecieveCursor = 0
|
||||||
|
local RecieveBuffer = buffer.create(64)
|
||||||
|
|
||||||
|
local RecieveInstances = {}
|
||||||
|
local RecieveInstanceCursor = 0
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
value: any,
|
||||||
|
next: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queue = {
|
||||||
|
head: Entry?,
|
||||||
|
tail: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferSave = {
|
||||||
|
Size: number,
|
||||||
|
Cursor: number,
|
||||||
|
Buffer: buffer,
|
||||||
|
Instances: {Instance}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function Read(Bytes: number)
|
||||||
|
local Offset = RecieveCursor
|
||||||
|
RecieveCursor += Bytes
|
||||||
|
return Offset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Save(): BufferSave
|
||||||
|
return {
|
||||||
|
Size = SendSize,
|
||||||
|
Cursor = SendCursor,
|
||||||
|
Buffer = SendBuffer,
|
||||||
|
Instances = SendInstances
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Load(Save: BufferSave?)
|
||||||
|
if Save then
|
||||||
|
SendSize = Save.Size
|
||||||
|
SendCursor = Save.Cursor
|
||||||
|
SendOffset = Save.Cursor
|
||||||
|
SendBuffer = Save.Buffer
|
||||||
|
SendInstances = Save.Instances
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
SendSize = 64
|
||||||
|
SendCursor = 0
|
||||||
|
SendOffset = 0
|
||||||
|
SendBuffer = buffer.create(64)
|
||||||
|
SendInstances = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Invoke()
|
||||||
|
if Invocations == 255 then
|
||||||
|
Invocations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local Invocation = Invocations
|
||||||
|
Invocations += 1
|
||||||
|
return Invocation
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Allocate(Bytes: number)
|
||||||
|
local InUse = (SendCursor + Bytes)
|
||||||
|
if InUse > SendSize then
|
||||||
|
--> Avoid resizing the buffer for every write
|
||||||
|
while InUse > SendSize do
|
||||||
|
SendSize *= 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
local Buffer = buffer.create(SendSize)
|
||||||
|
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||||
|
SendBuffer = Buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
SendOffset = SendCursor
|
||||||
|
SendCursor += Bytes
|
||||||
|
|
||||||
|
return SendOffset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CreateQueue(): Queue
|
||||||
|
return {
|
||||||
|
head = nil,
|
||||||
|
tail = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Pop(queue: Queue): any
|
||||||
|
local head = queue.head
|
||||||
|
if head == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.head = head.next
|
||||||
|
return head.value
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Push(queue: Queue, value: any)
|
||||||
|
local entry: Entry = {
|
||||||
|
value = value,
|
||||||
|
next = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.tail ~= nil then
|
||||||
|
queue.tail.next = entry
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.tail = entry
|
||||||
|
|
||||||
|
if queue.head == nil then
|
||||||
|
queue.head = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local Types = {}
|
||||||
|
local Calls = table.create(256)
|
||||||
|
|
||||||
|
local Events: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
local Queue: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue.Reliable[0] = table.create(256)
|
||||||
|
function Types.ReadEVENT_replicate_data(): (buffer)
|
||||||
|
-- Read BLOCK: 2 bytes
|
||||||
|
local BLOCK_START = Read(2)
|
||||||
|
local Length = buffer.readu16(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
local Value = buffer.create(Length)
|
||||||
|
buffer.copy(Value, 0, RecieveBuffer, Read(Length), Length)
|
||||||
|
return Value
|
||||||
|
end
|
||||||
|
|
||||||
|
function Types.WriteEVENT_replicate_data(Value: buffer): ()
|
||||||
|
-- Allocate BLOCK: 3 bytes
|
||||||
|
local BLOCK_START = Allocate(3)
|
||||||
|
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||||
|
local Length = buffer.len(Value)
|
||||||
|
buffer.writeu16(SendBuffer, BLOCK_START + 1, Length)
|
||||||
|
Allocate(Length)
|
||||||
|
buffer.copy(SendBuffer, SendOffset, Value, 0, Length)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not RunService:IsRunning() then
|
||||||
|
local NOOP = function() end
|
||||||
|
local Returns = table.freeze({
|
||||||
|
replicate_data = {
|
||||||
|
on = NOOP
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return Returns :: BLINK_EVENTS_SYMBOL
|
||||||
|
end
|
||||||
|
|
||||||
|
if not RunService:IsClient() then
|
||||||
|
error("Client network module can only be required from the client.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local Reliable: RemoteEvent = ReplicatedStorage:WaitForChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
||||||
|
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:WaitForChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||||
|
|
||||||
|
local function StepReplication()
|
||||||
|
if SendCursor <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local Buffer = buffer.create(SendCursor)
|
||||||
|
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||||
|
Reliable:FireServer(Buffer, SendInstances)
|
||||||
|
|
||||||
|
SendSize = 64
|
||||||
|
SendCursor = 0
|
||||||
|
SendOffset = 0
|
||||||
|
SendBuffer = buffer.create(64)
|
||||||
|
table.clear(SendInstances)
|
||||||
|
end
|
||||||
|
local Elapsed = 0
|
||||||
|
RunService.Heartbeat:Connect(function(DeltaTime: number)
|
||||||
|
Elapsed += DeltaTime
|
||||||
|
if Elapsed >= (1 / 61) then
|
||||||
|
Elapsed -= (1 / 61)
|
||||||
|
StepReplication()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
|
||||||
|
RecieveCursor = 0
|
||||||
|
RecieveBuffer = Buffer
|
||||||
|
RecieveInstances = Instances
|
||||||
|
RecieveInstanceCursor = 0
|
||||||
|
local Size = buffer.len(RecieveBuffer)
|
||||||
|
while (RecieveCursor < Size) do
|
||||||
|
-- Read BLOCK: 1 bytes
|
||||||
|
local BLOCK_START = Read(1)
|
||||||
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
if Index == 0 then
|
||||||
|
local Value: buffer = Types.ReadEVENT_replicate_data()
|
||||||
|
if Events.Reliable[0] ~= nil then
|
||||||
|
Events.Reliable[0](Value)
|
||||||
|
else
|
||||||
|
if #Queue.Reliable[0] > 256 then
|
||||||
|
warn("[Blink]: Event queue of \"replicate_data\" exceeded 256, did you forget to implement a listener?")
|
||||||
|
end
|
||||||
|
table.insert(Queue.Reliable[0], {Value} :: {any})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
Unreliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: {Instance})
|
||||||
|
RecieveCursor = 0
|
||||||
|
RecieveBuffer = Buffer
|
||||||
|
RecieveInstances = Instances
|
||||||
|
RecieveInstanceCursor = 0
|
||||||
|
local Size = buffer.len(RecieveBuffer)
|
||||||
|
while (RecieveCursor < Size) do
|
||||||
|
-- Read BLOCK: 1 bytes
|
||||||
|
local BLOCK_START = Read(1)
|
||||||
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
local Returns = table.freeze({
|
||||||
|
step_replication = StepReplication,
|
||||||
|
|
||||||
|
replicate_data = {
|
||||||
|
on = function(Listener: (Value: buffer) -> ()): () -> ()
|
||||||
|
Events.Reliable[0] = Listener
|
||||||
|
for Index, Arguments in Queue.Reliable[0] do
|
||||||
|
Listener(table.unpack(Arguments))
|
||||||
|
end
|
||||||
|
Queue.Reliable[0] = {}
|
||||||
|
return function (): ()
|
||||||
|
Events.Reliable[0] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
})
|
||||||
|
type BLINK_EVENTS_SYMBOL = typeof(Returns)
|
||||||
|
return Returns :: BLINK_EVENTS_SYMBOL
|
304
src/net/server.luau
Normal file
304
src/net/server.luau
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
--!strict
|
||||||
|
--!native
|
||||||
|
--!optimize 2
|
||||||
|
--!nolint LocalShadow
|
||||||
|
--#selene: allow(shadowing)
|
||||||
|
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
|
||||||
|
-- This file is not meant to be edited
|
||||||
|
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local Invocations = 0
|
||||||
|
|
||||||
|
local SendSize = 64
|
||||||
|
local SendOffset = 0
|
||||||
|
local SendCursor = 0
|
||||||
|
local SendBuffer = buffer.create(64)
|
||||||
|
local SendInstances = {}
|
||||||
|
|
||||||
|
local RecieveCursor = 0
|
||||||
|
local RecieveBuffer = buffer.create(64)
|
||||||
|
|
||||||
|
local RecieveInstances = {}
|
||||||
|
local RecieveInstanceCursor = 0
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
value: any,
|
||||||
|
next: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queue = {
|
||||||
|
head: Entry?,
|
||||||
|
tail: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferSave = {
|
||||||
|
Size: number,
|
||||||
|
Cursor: number,
|
||||||
|
Buffer: buffer,
|
||||||
|
Instances: {Instance}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function Read(Bytes: number)
|
||||||
|
local Offset = RecieveCursor
|
||||||
|
RecieveCursor += Bytes
|
||||||
|
return Offset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Save(): BufferSave
|
||||||
|
return {
|
||||||
|
Size = SendSize,
|
||||||
|
Cursor = SendCursor,
|
||||||
|
Buffer = SendBuffer,
|
||||||
|
Instances = SendInstances
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Load(Save: BufferSave?)
|
||||||
|
if Save then
|
||||||
|
SendSize = Save.Size
|
||||||
|
SendCursor = Save.Cursor
|
||||||
|
SendOffset = Save.Cursor
|
||||||
|
SendBuffer = Save.Buffer
|
||||||
|
SendInstances = Save.Instances
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
SendSize = 64
|
||||||
|
SendCursor = 0
|
||||||
|
SendOffset = 0
|
||||||
|
SendBuffer = buffer.create(64)
|
||||||
|
SendInstances = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Invoke()
|
||||||
|
if Invocations == 255 then
|
||||||
|
Invocations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local Invocation = Invocations
|
||||||
|
Invocations += 1
|
||||||
|
return Invocation
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Allocate(Bytes: number)
|
||||||
|
local InUse = (SendCursor + Bytes)
|
||||||
|
if InUse > SendSize then
|
||||||
|
--> Avoid resizing the buffer for every write
|
||||||
|
while InUse > SendSize do
|
||||||
|
SendSize *= 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
local Buffer = buffer.create(SendSize)
|
||||||
|
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||||
|
SendBuffer = Buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
SendOffset = SendCursor
|
||||||
|
SendCursor += Bytes
|
||||||
|
|
||||||
|
return SendOffset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CreateQueue(): Queue
|
||||||
|
return {
|
||||||
|
head = nil,
|
||||||
|
tail = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Pop(queue: Queue): any
|
||||||
|
local head = queue.head
|
||||||
|
if head == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.head = head.next
|
||||||
|
return head.value
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Push(queue: Queue, value: any)
|
||||||
|
local entry: Entry = {
|
||||||
|
value = value,
|
||||||
|
next = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.tail ~= nil then
|
||||||
|
queue.tail.next = entry
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.tail = entry
|
||||||
|
|
||||||
|
if queue.head == nil then
|
||||||
|
queue.head = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local Types = {}
|
||||||
|
local Calls = table.create(256)
|
||||||
|
|
||||||
|
local Events: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
local Queue: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Types.ReadEVENT_replicate_data(): (buffer)
|
||||||
|
-- Read BLOCK: 2 bytes
|
||||||
|
local BLOCK_START = Read(2)
|
||||||
|
local Length = buffer.readu16(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
local Value = buffer.create(Length)
|
||||||
|
buffer.copy(Value, 0, RecieveBuffer, Read(Length), Length)
|
||||||
|
return Value
|
||||||
|
end
|
||||||
|
|
||||||
|
function Types.WriteEVENT_replicate_data(Value: buffer): ()
|
||||||
|
-- Allocate BLOCK: 3 bytes
|
||||||
|
local BLOCK_START = Allocate(3)
|
||||||
|
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||||
|
local Length = buffer.len(Value)
|
||||||
|
buffer.writeu16(SendBuffer, BLOCK_START + 1, Length)
|
||||||
|
Allocate(Length)
|
||||||
|
buffer.copy(SendBuffer, SendOffset, Value, 0, Length)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not RunService:IsRunning() then
|
||||||
|
local NOOP = function() end
|
||||||
|
local Returns = table.freeze({
|
||||||
|
replicate_data = {
|
||||||
|
fire = NOOP,
|
||||||
|
fire_all = NOOP,
|
||||||
|
fire_list = NOOP,
|
||||||
|
fire_except = NOOP
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return Returns :: BLINK_EVENTS_SYMBOL
|
||||||
|
end
|
||||||
|
|
||||||
|
if not RunService:IsServer() then
|
||||||
|
error("Server network module can only be required from the server.")
|
||||||
|
end
|
||||||
|
|
||||||
|
local Reliable: RemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
||||||
|
if not Reliable then
|
||||||
|
local RemoteEvent = Instance.new("RemoteEvent")
|
||||||
|
RemoteEvent.Name = "BLINK_RELIABLE_REMOTE"
|
||||||
|
RemoteEvent.Parent = ReplicatedStorage
|
||||||
|
Reliable = RemoteEvent
|
||||||
|
end
|
||||||
|
|
||||||
|
local Unreliable: UnreliableRemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||||
|
if not Unreliable then
|
||||||
|
local UnreliableRemoteEvent = Instance.new("UnreliableRemoteEvent")
|
||||||
|
UnreliableRemoteEvent.Name = "BLINK_UNRELIABLE_REMOTE"
|
||||||
|
UnreliableRemoteEvent.Parent = ReplicatedStorage
|
||||||
|
Unreliable = UnreliableRemoteEvent
|
||||||
|
end
|
||||||
|
|
||||||
|
local PlayersMap: {[Player]: BufferSave} = {}
|
||||||
|
|
||||||
|
Players.PlayerRemoving:Connect(function(Player)
|
||||||
|
PlayersMap[Player] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function StepReplication()
|
||||||
|
for Player, Send in PlayersMap do
|
||||||
|
if Send.Cursor <= 0 then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local Buffer = buffer.create(Send.Cursor)
|
||||||
|
buffer.copy(Buffer, 0, Send.Buffer, 0, Send.Cursor)
|
||||||
|
Reliable:FireClient(Player, Buffer, Send.Instances)
|
||||||
|
|
||||||
|
Send.Size = 64
|
||||||
|
Send.Cursor = 0
|
||||||
|
Send.Buffer = buffer.create(64)
|
||||||
|
table.clear(Send.Instances)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RunService.Heartbeat:Connect(StepReplication)
|
||||||
|
Reliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
|
||||||
|
RecieveCursor = 0
|
||||||
|
RecieveBuffer = Buffer
|
||||||
|
RecieveInstances = Instances
|
||||||
|
RecieveInstanceCursor = 0
|
||||||
|
local Size = buffer.len(RecieveBuffer)
|
||||||
|
while (RecieveCursor < Size) do
|
||||||
|
-- Read BLOCK: 1 bytes
|
||||||
|
local BLOCK_START = Read(1)
|
||||||
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
Unreliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: {Instance})
|
||||||
|
RecieveCursor = 0
|
||||||
|
RecieveBuffer = Buffer
|
||||||
|
RecieveInstances = Instances
|
||||||
|
RecieveInstanceCursor = 0
|
||||||
|
local Size = buffer.len(RecieveBuffer)
|
||||||
|
while (RecieveCursor < Size) do
|
||||||
|
-- Read BLOCK: 1 bytes
|
||||||
|
local BLOCK_START = Read(1)
|
||||||
|
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
local Returns = table.freeze({
|
||||||
|
step_replication = StepReplication,
|
||||||
|
|
||||||
|
replicate_data = {
|
||||||
|
fire = function(Player: Player, Value: buffer): ()
|
||||||
|
Load(PlayersMap[Player])
|
||||||
|
Types.WriteEVENT_replicate_data(Value)
|
||||||
|
PlayersMap[Player] = Save()
|
||||||
|
end,
|
||||||
|
fire_all = function(Value: buffer): ()
|
||||||
|
Load()
|
||||||
|
Types.WriteEVENT_replicate_data(Value)
|
||||||
|
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||||
|
for _, Player in Players:GetPlayers() do
|
||||||
|
Load(PlayersMap[Player])
|
||||||
|
local Position = Allocate(Size)
|
||||||
|
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||||
|
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||||
|
PlayersMap[Player] = Save()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
fire_list = function(List: {Player}, Value: buffer): ()
|
||||||
|
Load()
|
||||||
|
Types.WriteEVENT_replicate_data(Value)
|
||||||
|
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||||
|
for _, Player in List do
|
||||||
|
Load(PlayersMap[Player])
|
||||||
|
local Position = Allocate(Size)
|
||||||
|
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||||
|
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||||
|
PlayersMap[Player] = Save()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
fire_except = function(Except: Player, Value: buffer): ()
|
||||||
|
Load()
|
||||||
|
Types.WriteEVENT_replicate_data(Value)
|
||||||
|
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||||
|
for _, Player in Players:GetPlayers() do
|
||||||
|
if Player == Except then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
Load(PlayersMap[Player])
|
||||||
|
local Position = Allocate(Size)
|
||||||
|
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||||
|
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||||
|
PlayersMap[Player] = Save()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
type BLINK_EVENTS_SYMBOL = typeof(Returns)
|
||||||
|
return Returns :: BLINK_EVENTS_SYMBOL
|
153
src/net/types.luau
Normal file
153
src/net/types.luau
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
--!strict
|
||||||
|
--!native
|
||||||
|
--!optimize 2
|
||||||
|
--!nolint LocalShadow
|
||||||
|
--#selene: allow(shadowing)
|
||||||
|
-- File generated by Blink v0.14.15 (https://github.com/1Axen/Blink)
|
||||||
|
-- This file is not meant to be edited
|
||||||
|
|
||||||
|
local Invocations = 0
|
||||||
|
|
||||||
|
local SendSize = 64
|
||||||
|
local SendOffset = 0
|
||||||
|
local SendCursor = 0
|
||||||
|
local SendBuffer = buffer.create(64)
|
||||||
|
local SendInstances = {}
|
||||||
|
|
||||||
|
local RecieveCursor = 0
|
||||||
|
local RecieveBuffer = buffer.create(64)
|
||||||
|
|
||||||
|
local RecieveInstances = {}
|
||||||
|
local RecieveInstanceCursor = 0
|
||||||
|
|
||||||
|
type Entry = {
|
||||||
|
value: any,
|
||||||
|
next: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queue = {
|
||||||
|
head: Entry?,
|
||||||
|
tail: Entry?
|
||||||
|
}
|
||||||
|
|
||||||
|
type BufferSave = {
|
||||||
|
Size: number,
|
||||||
|
Cursor: number,
|
||||||
|
Buffer: buffer,
|
||||||
|
Instances: {Instance}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function Read(Bytes: number)
|
||||||
|
local Offset = RecieveCursor
|
||||||
|
RecieveCursor += Bytes
|
||||||
|
return Offset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Save(): BufferSave
|
||||||
|
return {
|
||||||
|
Size = SendSize,
|
||||||
|
Cursor = SendCursor,
|
||||||
|
Buffer = SendBuffer,
|
||||||
|
Instances = SendInstances
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Load(Save: BufferSave?)
|
||||||
|
if Save then
|
||||||
|
SendSize = Save.Size
|
||||||
|
SendCursor = Save.Cursor
|
||||||
|
SendOffset = Save.Cursor
|
||||||
|
SendBuffer = Save.Buffer
|
||||||
|
SendInstances = Save.Instances
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
SendSize = 64
|
||||||
|
SendCursor = 0
|
||||||
|
SendOffset = 0
|
||||||
|
SendBuffer = buffer.create(64)
|
||||||
|
SendInstances = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Invoke()
|
||||||
|
if Invocations == 255 then
|
||||||
|
Invocations = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local Invocation = Invocations
|
||||||
|
Invocations += 1
|
||||||
|
return Invocation
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Allocate(Bytes: number)
|
||||||
|
local InUse = (SendCursor + Bytes)
|
||||||
|
if InUse > SendSize then
|
||||||
|
--> Avoid resizing the buffer for every write
|
||||||
|
while InUse > SendSize do
|
||||||
|
SendSize *= 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
local Buffer = buffer.create(SendSize)
|
||||||
|
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||||
|
SendBuffer = Buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
SendOffset = SendCursor
|
||||||
|
SendCursor += Bytes
|
||||||
|
|
||||||
|
return SendOffset
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CreateQueue(): Queue
|
||||||
|
return {
|
||||||
|
head = nil,
|
||||||
|
tail = nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Pop(queue: Queue): any
|
||||||
|
local head = queue.head
|
||||||
|
if head == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.head = head.next
|
||||||
|
return head.value
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Push(queue: Queue, value: any)
|
||||||
|
local entry: Entry = {
|
||||||
|
value = value,
|
||||||
|
next = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.tail ~= nil then
|
||||||
|
queue.tail.next = entry
|
||||||
|
end
|
||||||
|
|
||||||
|
queue.tail = entry
|
||||||
|
|
||||||
|
if queue.head == nil then
|
||||||
|
queue.head = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local Types = {}
|
||||||
|
local Calls = table.create(256)
|
||||||
|
|
||||||
|
local Events: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
local Queue: any = {
|
||||||
|
Reliable = table.create(256),
|
||||||
|
Unreliable = table.create(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
15
src/server/data.luau
Normal file
15
src/server/data.luau
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
--!strict
|
||||||
|
local sapphire_data = require("@pkg/sapphire_data")
|
||||||
|
|
||||||
|
export type data = {
|
||||||
|
coins: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
local template: data = {
|
||||||
|
coins = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sapphire_data.server({
|
||||||
|
template = template,
|
||||||
|
store_name = "player_data",
|
||||||
|
})
|
16
src/server/main.server.luau
Normal file
16
src/server/main.server.luau
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
--!strict
|
||||||
|
local sapphire = require("@pkg/sapphire")
|
||||||
|
|
||||||
|
local data = require("@server/data")
|
||||||
|
local ecs = require("@pkg/sapphire_jecs")
|
||||||
|
local lifecycles = require("@pkg/sapphire_lifecycles")
|
||||||
|
|
||||||
|
local services = script.Parent.services
|
||||||
|
|
||||||
|
sapphire()
|
||||||
|
:register_singletons(services)
|
||||||
|
:register_singletons(services.systems)
|
||||||
|
:use(data)
|
||||||
|
:use(ecs)
|
||||||
|
:use(lifecycles)
|
||||||
|
:start()
|
22
src/server/services/replicate_data.luau
Normal file
22
src/server/services/replicate_data.luau
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
--!strict
|
||||||
|
local data = require("@server/data")
|
||||||
|
local net = require("@net/server")
|
||||||
|
|
||||||
|
local function init()
|
||||||
|
-- this fires every time the data is changed including initial data load
|
||||||
|
data.on_data_changed(function(player)
|
||||||
|
local difference = data.calculate_difference(player)
|
||||||
|
if not difference then
|
||||||
|
-- weird.. but ok
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
net.replicate_data.fire(player, difference)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
init = init,
|
||||||
|
-- data is important
|
||||||
|
priority = math.huge,
|
||||||
|
}
|
31
src/server/services/systems/entitify_players.luau
Normal file
31
src/server/services/systems/entitify_players.luau
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
--!strict
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
|
||||||
|
local ecs = require("@pkg/sapphire_jecs")
|
||||||
|
local ref = ecs.ref
|
||||||
|
|
||||||
|
local components = require("@shared/components")
|
||||||
|
local cplayer = components.player
|
||||||
|
|
||||||
|
local types = require("@shared/types")
|
||||||
|
local util = require("@shared/util")
|
||||||
|
type player = types.RobloxPlayer
|
||||||
|
type character = types.R15Character
|
||||||
|
|
||||||
|
local player_added = ecs.collect(Players.PlayerAdded)
|
||||||
|
local player_removing = ecs.collect(Players.PlayerRemoving)
|
||||||
|
|
||||||
|
local function system()
|
||||||
|
for _, player: player in player_added do
|
||||||
|
ref(player.UserId):set(cplayer, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, player: player in player_removing do
|
||||||
|
ref(player.UserId):delete()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
system = util.wrap_system(system),
|
||||||
|
phase = "heartbeat",
|
||||||
|
}
|
33
src/shared/components.luau
Normal file
33
src/shared/components.luau
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
--!strict
|
||||||
|
local ecs = require("@pkg/sapphire_jecs")
|
||||||
|
local jecs = ecs.jecs
|
||||||
|
local world = ecs.world
|
||||||
|
type entity<T = nil> = ecs.entity<T>
|
||||||
|
|
||||||
|
local types = require("@shared/types")
|
||||||
|
-- change the `Character` type in `types` to whichever character your game is using
|
||||||
|
type character = types.Character
|
||||||
|
type player = types.RobloxPlayer<character>
|
||||||
|
|
||||||
|
return {
|
||||||
|
player = world:component() :: entity<player>,
|
||||||
|
|
||||||
|
world_model = world:component() :: entity<BasePart>,
|
||||||
|
|
||||||
|
-- jecs reexports
|
||||||
|
on_add = jecs.OnAdd,
|
||||||
|
on_remove = jecs.OnRemove,
|
||||||
|
on_set = jecs.OnSet,
|
||||||
|
child_of = jecs.ChildOf,
|
||||||
|
componente = jecs.Component,
|
||||||
|
wildcard = jecs.Wildcard,
|
||||||
|
w = jecs.Wildcard,
|
||||||
|
on_delete = jecs.OnDelete,
|
||||||
|
on_delete_target = jecs.OnDeleteTarget,
|
||||||
|
delete = jecs.Delete,
|
||||||
|
remove = jecs.Remove,
|
||||||
|
name = jecs.Name,
|
||||||
|
rest = jecs.Rest,
|
||||||
|
|
||||||
|
pair = jecs.pair,
|
||||||
|
}
|
182
src/shared/types.luau
Normal file
182
src/shared/types.luau
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
--!strict
|
||||||
|
export type R15CharacterChildren = {
|
||||||
|
Humanoid: Humanoid & {
|
||||||
|
Animator: Animator,
|
||||||
|
},
|
||||||
|
Shirt: Shirt,
|
||||||
|
Pants: Pants,
|
||||||
|
["Body Colors"]: BodyColors,
|
||||||
|
["Shirt Graphic"]: ShirtGraphic,
|
||||||
|
HumanoidRootPart: Part & {
|
||||||
|
Climbing: Sound,
|
||||||
|
Died: Sound,
|
||||||
|
FreeFalling: Sound,
|
||||||
|
GettingUp: Sound,
|
||||||
|
Jumping: Sound,
|
||||||
|
Landing: Sound,
|
||||||
|
Running: Sound,
|
||||||
|
Splash: Sound,
|
||||||
|
Swimming: Sound,
|
||||||
|
RootRigAttachment: Attachment,
|
||||||
|
},
|
||||||
|
Head: MeshPart & {
|
||||||
|
FaceCenterAttachment: Attachment,
|
||||||
|
FaceFrontAttachment: Attachment,
|
||||||
|
HairAttachment: Attachment,
|
||||||
|
HatAttachment: Attachment,
|
||||||
|
NeckRigAttachment: Attachment,
|
||||||
|
Neck: Motor6D,
|
||||||
|
},
|
||||||
|
LeftFoot: MeshPart & {
|
||||||
|
LeftAnkleRigAttachment: Attachment,
|
||||||
|
LeftFootAttachment: Attachment,
|
||||||
|
LeftFoot: WrapTarget,
|
||||||
|
LeftAnkle: Motor6D,
|
||||||
|
},
|
||||||
|
RightFoot: MeshPart & {
|
||||||
|
RightAnkleRigAttachment: Attachment,
|
||||||
|
RightFootAttachment: Attachment,
|
||||||
|
RightFoot: WrapTarget,
|
||||||
|
RightAnkle: Motor6D,
|
||||||
|
},
|
||||||
|
LeftLowerLeg: MeshPart & {
|
||||||
|
LeftLowerLeg: WrapTarget,
|
||||||
|
},
|
||||||
|
RightLowerLeg: MeshPart & {
|
||||||
|
RightLowerLeg: WrapTarget,
|
||||||
|
},
|
||||||
|
LeftUpperLeg: MeshPart & {
|
||||||
|
LeftUpperLeg: WrapTarget,
|
||||||
|
},
|
||||||
|
RightUpperLeg: MeshPart & {
|
||||||
|
RightUpperLeg: WrapTarget,
|
||||||
|
},
|
||||||
|
LowerTorso: MeshPart & {
|
||||||
|
LowerTorso: WrapTarget,
|
||||||
|
},
|
||||||
|
UpperTorso: MeshPart & {
|
||||||
|
UpperTorso: WrapTarget,
|
||||||
|
},
|
||||||
|
RightHand: MeshPart & {
|
||||||
|
RightHand: WrapTarget,
|
||||||
|
},
|
||||||
|
LeftHand: MeshPart & {
|
||||||
|
LeftHand: WrapTarget,
|
||||||
|
},
|
||||||
|
RightLowerArm: MeshPart & {
|
||||||
|
RightLowerArm: WrapTarget,
|
||||||
|
},
|
||||||
|
RightUpperArm: MeshPart & {
|
||||||
|
RightUpperArm: WrapTarget,
|
||||||
|
},
|
||||||
|
LeftLowerArm: MeshPart & {
|
||||||
|
LeftElbowRigAttachment: Attachment,
|
||||||
|
LeftWristAttachment: Attachment,
|
||||||
|
LeftLowerArm: WrapTarget,
|
||||||
|
LeftElbow: Motor6D,
|
||||||
|
},
|
||||||
|
LeftUpperArm: MeshPart & {
|
||||||
|
LeftElbowRigAttachment: Attachment,
|
||||||
|
LeftShoulderAttachment: Attachment,
|
||||||
|
LeftShoulderRigAttachment: Attachment,
|
||||||
|
LeftUpperArm: WrapTarget,
|
||||||
|
LeftShoulder: Motor6D,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export type R6CharacterChildren = {
|
||||||
|
Humanoid: Humanoid & {
|
||||||
|
Animator: Animator,
|
||||||
|
},
|
||||||
|
Shirt: Shirt,
|
||||||
|
Pants: Pants,
|
||||||
|
["Body Colors"]: BodyColors,
|
||||||
|
["Shirt Graphic"]: ShirtGraphic,
|
||||||
|
HumanoidRootPart: Part & {
|
||||||
|
Climbing: Sound,
|
||||||
|
Died: Sound,
|
||||||
|
FreeFalling: Sound,
|
||||||
|
GettingUp: Sound,
|
||||||
|
Jumping: Sound,
|
||||||
|
Landing: Sound,
|
||||||
|
Running: Sound,
|
||||||
|
Splash: Sound,
|
||||||
|
Swimming: Sound,
|
||||||
|
RootAttachment: Attachment,
|
||||||
|
RootJoint: Motor6D,
|
||||||
|
},
|
||||||
|
Head: Part & {
|
||||||
|
FaceCenterAttachment: Attachment,
|
||||||
|
FaceFrontAttachment: Attachment,
|
||||||
|
HairAttachment: Attachment,
|
||||||
|
HatAttachment: Attachment,
|
||||||
|
Mesh: SpecialMesh,
|
||||||
|
face: Decal,
|
||||||
|
},
|
||||||
|
Torso: Part & {
|
||||||
|
BodyBackAttachment: Attachment,
|
||||||
|
BodyFrontAttachment: Attachment,
|
||||||
|
LeftCollarAttachment: Attachment,
|
||||||
|
NeckAttachment: Attachment,
|
||||||
|
RightCollarAttachment: Attachment,
|
||||||
|
WaistBackAttachment: Attachment,
|
||||||
|
WaistCenterAttachment: Attachment,
|
||||||
|
WaistFrontAttachment: Attachment,
|
||||||
|
roblox: Decal,
|
||||||
|
["Left Hip"]: Motor6D,
|
||||||
|
["Left Shoulder"]: Motor6D,
|
||||||
|
Neck: Motor6D,
|
||||||
|
["Right Hip"]: Motor6D,
|
||||||
|
["Right Shoulder"]: Motor6D,
|
||||||
|
},
|
||||||
|
["Left Arm"]: Part & {
|
||||||
|
LeftGripAttachment: Attachment,
|
||||||
|
LeftShoulderAttachment: Attachment,
|
||||||
|
},
|
||||||
|
["Left Leg"]: Part & {
|
||||||
|
LeftFootAttachment: Attachment,
|
||||||
|
},
|
||||||
|
["Right Arm"]: Part & {
|
||||||
|
RightGripAttachment: Attachment,
|
||||||
|
RightShoulderAttachment: Attachment,
|
||||||
|
},
|
||||||
|
["Right Leg"]: Part & {
|
||||||
|
RightFootAttachment: Attachment,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export type R15Character = Model & Instance & R15CharacterChildren
|
||||||
|
export type R6Character = Model & Instance & R6CharacterChildren
|
||||||
|
-- change it to whatever youre using
|
||||||
|
export type Character = R15Character
|
||||||
|
|
||||||
|
export type RobloxPlayer<Character = Character> = Player & {
|
||||||
|
PlayerGui: PlayerGui & StarterGui & {
|
||||||
|
BubbleChat: ScreenGui,
|
||||||
|
Chat: ScreenGui,
|
||||||
|
Freecam: ScreenGui,
|
||||||
|
},
|
||||||
|
PlayerScripts: PlayerScripts & {
|
||||||
|
BubbleChat: LocalScript,
|
||||||
|
ChatScript: LocalScript,
|
||||||
|
PlayerScriptsLoader: LocalScript,
|
||||||
|
RbxCharacterSounds: LocalScript,
|
||||||
|
PlayerModule: ModuleScript,
|
||||||
|
},
|
||||||
|
Character: Character?,
|
||||||
|
CharacterAdded: RBXScriptSignal<Character>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Ok<T> = {
|
||||||
|
kind: "ok",
|
||||||
|
value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Err<E> = {
|
||||||
|
kind: "err",
|
||||||
|
value: E,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Result<T, E> = Ok<T> | Err<E>
|
||||||
|
|
||||||
|
return {}
|
28
src/shared/util.luau
Normal file
28
src/shared/util.luau
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--!strict
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
Useful for wrapping systems for schedulers which take a function returning a system.
|
||||||
|
|
||||||
|
(
|
||||||
|
```lua
|
||||||
|
-- for example
|
||||||
|
local function system(world: world)
|
||||||
|
return function(dt: number)
|
||||||
|
...
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
)
|
||||||
|
|
||||||
|
@param system
|
||||||
|
@return () -> (T...) -> ()
|
||||||
|
]=]
|
||||||
|
local function wrap_system<T...>(system: (T...) -> ()): () -> (T...) -> ()
|
||||||
|
return function()
|
||||||
|
return system
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
wrap_system = wrap_system,
|
||||||
|
}
|
10
stylua.toml
Normal file
10
stylua.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
column_width = 120
|
||||||
|
line_endings = "Unix"
|
||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 4
|
||||||
|
quote_style = "AutoPreferDouble"
|
||||||
|
call_parentheses = "Input"
|
||||||
|
collapse_simple_statement = "Never"
|
||||||
|
|
||||||
|
[sort_requires]
|
||||||
|
enabled = true
|
Loading…
Reference in a new issue