Skip to content

Commit aef2fbc

Browse files
authored
Merge pull request #4 from AtomicGmod/develop
feat(mysql): add mysql support, update readme, add code style, etc
2 parents a3f96e6 + 3cbb437 commit aef2fbc

7 files changed

Lines changed: 258 additions & 36 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
changelog.md
22
puff.yml
3+
docker-compose.yml
34
.env

CODE_STYLE.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Atomic Framework — Code Style Guide
2+
3+
This document defines the official coding conventions for **Atomic Framework** and all packages built under it.
4+
The goal is simple — **clean, readable, and predictable Lua code**.
5+
6+
---
7+
8+
## 🧱 General Style
9+
10+
- **Indentation:** 2 spaces (no tabs)
11+
- **Line length:** ~100 characters max
12+
- **Encoding:** UTF-8 (no BOM)
13+
- **Trailing whitespace:** none
14+
- **End of file:** one empty line
15+
- **Comments:** short, factual, no essays
16+
17+
---
18+
19+
## 📦 Naming Conventions
20+
21+
| Type | Style | Example |
22+
|------|--------|---------|
23+
| Variables / locals | `camelCase` | `playerData`, `configTable` |
24+
| Functions | `camelCase` | `loadPackage`, `registerCommand` |
25+
| Classes / Objects | `PascalCase` | `Package`, `NetworkService` |
26+
| Constants | `ALL_CAPS` | `DEFAULT_TIMEOUT` |
27+
| Private internals | Prefix `_` | `_resolve_dependencies` |
28+
| Package namespaces | Lowercase, dot-separated | `atomic.config`, `atomic.command` |
29+
30+
## 🧠 OOP Conventions
31+
32+
- Use `:` for instance methods, `.` for static functions.
33+
- Avoid global variables — everything should live under a namespace (`Atomic.*`).
34+
- Keep internal state private whenever possible.
35+
- Don’t mutate other modules’ tables directly.
36+
- Favor composition over inheritance when possible.
37+
38+
---
39+
40+
## 🧩 Documentation & Comments
41+
42+
- Use `--` for single-line comments and `--[[ ... ]]` for multi-line blocks.
43+
- Document **every public function** — parameters and return values.
44+
- Keep comment style consistent and concise.
45+
46+
## 🚫 Don’ts
47+
48+
- ❌ Do **not** use global variables unless absolutely necessary.
49+
- ❌ Do **not** rely on random metatable hacks or implicit magic.
50+
- ❌ Do **not** use inconsistent indentation or mixed styles.
51+
52+
---
53+
54+
## 🧩 Commits & Versioning
55+
56+
Use clear and conventional commit prefixes:
57+
58+
| Type | Description | Example |
59+
|------|------------|---------|
60+
| `feat:` | New feature | `feat(command): add argument parser` |
61+
| `fix:` | Bug fix | `fix(config): prevent nil index in mysql adapter` |
62+
| `refactor:` | Code improvement | `refactor(package): simplify dependency resolver` |
63+
| `docs:` | Documentation changes | `docs(readme): update installation guide` |
64+
| `chore:` | Non-code tasks | `chore: cleanup old debug prints` |

README.md

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
11
# Atomic Framework
2-
No more magic
3-
4-
## To Do
5-
### Packages
6-
- [x] Hooks system
7-
- [x] Commands system
8-
- [x] Package unloading
9-
- [ ] Config system (with value saving in database)
10-
11-
### Libraries
12-
- [x] ``atomic.class``
13-
- [ ] ``atomic.database``
14-
- [ ] ``atomic.config``
15-
- [ ] ``atomic.net`` (autumnnet as base)
16-
- [ ] ``atomic.web``
17-
- [x] ``atomic.command``
18-
- [ ] Command parser
19-
- [x] ``atomic.bind``
20-
21-
### Misc
22-
- [ ] Normal documentation everywhere
2+
**No more magic.**
3+
4+
Atomic is a flexible, OOP-driven framework for building Garry’s Mod addons and gamemodes with a clean, modular architecture.
5+
Each **package** is a self-contained unit — just like an addon — with built-in dependency management and powerful libraries for writing structured, maintainable Lua code.
6+
7+
---
8+
9+
## Features
10+
- Modular **package-based** architecture
11+
- Built-in **dependency resolution**
12+
- Flexible **OOP** utilities and core libraries
13+
- Expandable with **atomic.\*** modules (config, command, network, etc.)
14+
- Designed for both **addons** and **gamemodes**
15+
16+
---
17+
18+
## Documentation
19+
Full documentation, API references, and examples are available here:
20+
👉 **[Atomic Framework Wiki](https://github.com/AtomicGmod/atomic-framework/wiki)**
21+
22+
---
23+
24+
## Installation
25+
Optional dependencies:
26+
- For `atomic.mysql` → install [MySQLOO](https://github.com/FredyH/MySQLOO)
27+
- For `atomic.git` → install [gm_git](https://github.com/AtomicGmod/gm_git)
28+
29+
Simply place the framework in your `addons/` folder.
30+
Atomic will automatically load and register available packages.
31+
32+
---
33+
34+
## Contributing
35+
We welcome all contributions — bug fixes, improvements, and new libraries are appreciated.
36+
Please follow the [project’s coding style](./CODE_STYLE.md) and submit a pull request through GitHub.
37+
Discussions and proposals are also encouraged in the issue tracker.
38+
39+
---
40+
41+
## Roadmap
42+
See [To Do](#) or the project’s issue tracker for current development progress.
43+
44+
---
45+
46+
## License
47+
This project is licensed under the **GNU General Public License v3.0**.
48+
See the [LICENSE](./LICENSE) file for more details.

lua/atomic/libraries/git.lua

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
1-
if (not util.IsBinaryModuleInstalled("atomic_git")) then
2-
atomic.log:warn("Binary module `atomic_git` isn't installed!")
3-
1+
if (not util.IsBinaryModuleInstalled("git")) then
42
return
53
end
64

75
---@class Atomic.Git.Folder
86
---@field folder string
97

10-
if (not atomic.git) then
11-
require("atomic_git")
12-
13-
if (not atomic.git) then
14-
return
15-
end
8+
if (not git) then
9+
require("git")
1610
end
1711

12+
atomic.git = atomic.git or {
13+
logger = atomic.logger.new("git")
14+
}
1815

1916
---@type table<string, fun(name: string): Atomic.Git.Folder>
2017
local openers = {
2118
root = function()
22-
return atomic.git.open("garrysmod")
19+
return git.open("garrysmod")
2320
end,
2421

2522
gamemode = function(name)
26-
return atomic.git.open("garrysmod/gamemodes/" .. name)
23+
return git.open("garrysmod/gamemodes/" .. name)
2724
end,
2825

2926
addon = function(name)
30-
return atomic.git.open("garrysmod/addons/" .. name)
27+
return git.open("garrysmod/addons/" .. name)
3128
end
3229
}
3330

@@ -58,7 +55,8 @@ function atomic.git.from(kind, name)
5855
local folder, err = opener(name)
5956

6057
if (not folder) then
61-
return atomic.git.logger:err("failed to open git repository: %s", tostring(err))
58+
local who = name and kind .. " " .. name or kind
59+
return atomic.git.logger:err("failed to open git repository for %s: %s", who, tostring(err))
6260
end
6361

6462
atomic.git._storage[kind][name] = folder

lua/atomic/libraries/mysql.lua

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
if (not util.IsBinaryModuleInstalled("mysqloo")) then
2+
return
3+
end
4+
5+
if (not mysqloo) then
6+
require("mysqloo")
7+
end
8+
9+
atomic.mysql = atomic.mysql or {
10+
logger = atomic.logger.new("mysql")
11+
}
12+
13+
local logger = atomic.mysql.logger
14+
15+
file.CreateDir("atomic/mysql")
16+
17+
if (file.Size("data/atomic/mysql/credentials.json", "GAME") <= 0) then
18+
local defaultJson = [[{
19+
"host": "127.0.0.1",
20+
"user": "root",
21+
"password": "root",
22+
"table": "gmod",
23+
"port": 3306
24+
}]];
25+
26+
file.Write("atomic/mysql/credentials.json", defaultJson)
27+
logger:warn("The `credentials.json` file has just been created in `data/atomic/mysql/` folder. To work with MySQL, fill in the database connection credentials in this file.")
28+
end
29+
30+
---@class Atomic.MySQL.Credentials
31+
---@field host string
32+
---@field user string
33+
---@field password string
34+
---@field table string
35+
---@field port? integer
36+
local credentials = util.JSONToTable(file.Read("atomic/mysql/credentials.json"))
37+
local autoconnect = CreateConVar("atomic_mysql_autoconnect", "1", {FCVAR_PROTECTED, FCVAR_ARCHIVE}, "Should the connection to MySQL be automatic?")
38+
39+
if (autoconnect:GetBool() and not atomic.mysql._database) then
40+
atomic.mysql._database = mysqloo.connect(
41+
credentials.host,
42+
credentials.user,
43+
credentials.password,
44+
credentials.table,
45+
credentials.port or 3306
46+
)
47+
48+
atomic.mysql._database.onConnected = function(_)
49+
logger:info("Successfully connected to the MySQL database")
50+
end
51+
52+
atomic.mysql._database.onConnectionFailed = function(_, err)
53+
logger:err("Error while connecting to database: %s", err)
54+
end
55+
56+
atomic.mysql._database.onError = function(_, err, sql)
57+
logger:debug("Error executing query `%s`: %s", sql, err)
58+
end
59+
60+
atomic.mysql._database:connect()
61+
end
62+
63+
local prepareTypes = {
64+
string = "setString",
65+
boolean = "setBoolean",
66+
number = "setNumber"
67+
}
68+
69+
--- Returns a raw database object
70+
---@see https://github.com/FredyH/MySQLOO/
71+
function atomic.mysql.getDatabase()
72+
return atomic.mysql._database
73+
end
74+
75+
--- Checks whether the connection to the database is active.
76+
---@return boolean
77+
function atomic.mysql.isConnected()
78+
return atomic.mysql._database and atomic.mysql._database:ping()
79+
end
80+
81+
--- Executes a query to the database, and escaping the arguments
82+
---
83+
--- ```lua
84+
--- local data = atomic.mysqlo.query("SELECT * from users WHERE steamid=?;", Player(1):SteamID())
85+
--- ```
86+
---@async
87+
---@param query string
88+
---@vararg ...?
89+
---@return table, string?
90+
function atomic.mysql.query(query, ...)
91+
local co = coroutine.get()
92+
93+
local result, error
94+
local prepared = atomic.mysql._database:prepare(query)
95+
prepared.onSuccess = function(_, data)
96+
result = data
97+
98+
coroutine.resume(co)
99+
end
100+
101+
prepared.onError = function(_, err)
102+
error = err
103+
104+
coroutine.resume(co)
105+
end
106+
107+
for index, value in ipairs({...}) do
108+
local method = prepareTypes[type(value)] or prepareTypes.string
109+
prepared[method](prepared, index, value)
110+
end
111+
112+
prepared:start()
113+
114+
coroutine.yield()
115+
116+
return result, error
117+
end

lua/atomic/utils/coroutine.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,19 @@
22
---@return boolean
33
function coroutine.start(fun)
44
return coroutine.resume(coroutine.create(fun))
5+
end
6+
7+
--- Checks whether the current function is within a coroutine or not, and if not, throws an error.
8+
---
9+
--- ```lua
10+
--- ```
11+
---@return thread
12+
function coroutine.get()
13+
local co = coroutine.running()
14+
15+
if (not co) then
16+
error("attempt to use an asynchronous function outside of a coroutine")
17+
end
18+
19+
return co
520
end

lua/autorun/atomic_autorun.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
atomic = {
22
meta = {
33
author = "smokingplaya",
4-
version_name = "Feijoa",
5-
version = "0.2.1",
4+
version_name = "Ackee",
5+
version = "0.3.0",
66
}
77
}
88

@@ -31,6 +31,7 @@ shared("atomic/utils/semver.lua")
3131
shared("atomic/libraries/bind.lua")
3232
client("atomic/libraries/web.lua")
3333
server("atomic/libraries/command.lua")
34+
server("atomic/libraries/mysql.lua")
3435
server("atomic/libraries/git.lua")
3536
-- package loading should be latest
3637
shared("atomic/libraries/package/class.lua")

0 commit comments

Comments
 (0)