Server Files

This commit is contained in:
Hopeless YABO
2026-03-31 19:36:44 +02:00
parent d29c93c30d
commit af039b0504
36 changed files with 4265 additions and 2 deletions
+28
View File
@@ -0,0 +1,28 @@
# Apex Freeroam — GTA V RAGE:MP Server
# ──────────────── Node.js ────────────────
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# ──────────────── Build Outputs ──────────
client_packages/cef/
client_packages/client.js
# ──────────────── Environment / Secrets ──
settings.json
# ──────────────── RAGE:MP Server ─────────
ragemp-server.exe
BugTrap-x64.dll
*.dll
*.so
*.dylib
maps/
# ──────────────── IDE / OS ───────────────
.vscode/
.idea/
.DS_Store
Thumbs.db
+171 -2
View File
@@ -1,2 +1,171 @@
# YabosRageMPCore
Everything you need to start your RageMP RP, DM or Freeroam server using performance friendly code + Rage-RPC
<div align="center">
# 🔥 YabosRageMPCore
### A open-source **RAGE:MP** GTA V multiplayer server framework.
[![License: GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![RAGE:MP](https://img.shields.io/badge/Platform-RAGE%3AMP-red)](https://rage.mp/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript)](https://www.typescriptlang.org/)
[![Vue.js](https://img.shields.io/badge/Vue-3.x-42b883?logo=vue.js)](https://vuejs.org/)
[![Vite](https://img.shields.io/badge/Vite-5.x-646CFF?logo=vite)](https://vitejs.dev/)
_A modern, scalable server boilerplate with a type-safe RPC system and a Hot-Module-Reloading CEF UI pipeline._
</div>
---
## ✨ Features
- **Type-safe RPC** across Server, Client, and CEF using [`@entityseven/rage-fw-rpc`](https://github.com/entityseven/rage-framework)
- **Split architecture** — clean separation between server logic, client scripts, and UI
- **Modern UI pipeline** — Vue 3 + Vite with Hot Module Replacement for rapid development
- **Custom Chat UI** — in-game chat built in Vue with support for player, global, admin, system, and error messages
- **Smart CEF routing** — automatically routes CEF to `localhost:3000` (dev) or the bundled package (production) via `settings.json`
- **Zero boilerplate overhead** — only the code that matters
---
## 📁 Project Structure
```
YabosRageMPCore/
├── packages/
│ └── core/ # Server-side logic (Node.js)
│ └── index.js # Entry point: RPC handlers, player events
├── develop_client/ # Client-side TypeScript source
│ └── src/
│ └── index.ts # Compiled to client_packages/client.js
├── develop_cef/ # Vue 3 SPA for all in-game UI
│ └── src/
│ ├── App.vue
│ └── components/
│ └── Chat.vue # Custom chat system
├── client_packages/ # [BUILD OUTPUT] — loaded by RAGE:MP
│ ├── index.js # Loader entrypoint
│ ├── client.js # Compiled client bundle
│ └── cef/ # Compiled Vue SPA
└── settings.json # Database, debug, and CEF environment config
```
---
## 🚀 Getting Started
### Prerequisites
- [Node.js](https://nodejs.org/) >= 18.x
- [RAGE:MP Server](https://rage.mp/)
### 1. Clone the repository
```bash
git clone https://github.com/yabooo666/YabosRageMPCore
cd YabosRageMPCore
```
### 2. Configure `settings.json`
```json
{
"mysql": {
"host": "localhost",
"port": 3306,
"user": "root",
"password": "",
"database": "ragempcore"
},
"debugger": true,
"cef": "dev"
}
```
> Set `"cef": "dev"` to use the Vite dev server, `"cef": "build"` to use the compiled bundle.
### 3. Install dependencies
```bash
# Server core
npm install
# Client script
cd ../../develop_client && npm install
# CEF UI
cd ../develop_cef && npm install
```
### 4a. Development Mode (with HMR)
```bash
# Terminal 1 — Watch & compile client script
cd develop_client && npm run dev
# Terminal 2 — Start Vite dev server for CEF
cd develop_cef && npm run dev
```
### 4b. Production Mode
```bash
# Compile client script
cd develop_client && npm run build
# Compile CEF UI
cd develop_cef && npm run build
# Start the RAGE:MP server
./ragemp-server.exe
```
---
## 🔌 RPC Communication
Cross-environment communication is handled by [RageFW RPC](https://github.com/entityseven/rage-framework).
| From | To | Method |
| ------ | ------ | ----------------------------------------- |
| Server | Client | `rpc.callClient(player, 'event', [args])` |
| Client | Server | `rpc.callServer('event', [args])` |
| Client | CEF | `rpc.callBrowser('event', [args])` |
| CEF | Client | `chatRpc.callClient('event', [args])` |
To register a handler:
```js
rpc.register("my:event", (player, arg1) => {
// handle it
return true;
});
```
---
## 📄 License
This project is licensed under the **GNU General Public License v3.0**.
See the [LICENSE](LICENSE) file for details.
```
YabosRageMPCore — RAGE:MP Server Framework
Copyright (C) 2025
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
```
---
<div align="center">
**Made with ❤️ for the RAGE:MP community**
</div>
+633
View File
@@ -0,0 +1,633 @@
olp啦 牁su鄍P蜂;椚_tM檀=nfe譥
𠙶fco癯)
olp徉
.i漖_ 值{縳
pt.s鶧xs鸇4啦 t煻 俳 錏l s啦 .i漖_[r邲
f偯r)er皡r;尠r 值m鉐ulxs鸇4t煻;pt.牁ht漟 值r;尠ru s徉}徉
n sa 值m鉐ulelXa6太
ls偏eo瘈 錏cuo熏x暫y暫z嗷{f偯te 糔!倘 nfe譪)
i蒩x啦 駏|噿0撻;h.橛= 螩0徉ts 值z|啡.
罿eef偯te 駏=倘 bc餀)
萱rysr(餑)
i蒩x啦 鞈0邲
i蒩y啦 鞈1邲
i蒩z啦 鞈2邲
篸ee
i蒩x啦 駜x徉ts 值x;h.糔=.縎
罿}
ete蜂 錏ru w偏eo瘈(h.餗 h.樾 h.篪;
d)
sno鶲Vt3嗷ru w偏eo瘈(i蒩x冕 磔x暫ts 銀v,h.糔+.篪;l tne褙Vt3h.駏+,h.橛+,h.糔+)徉}urt)
sno鶲Vt3嗷ru w偏eo瘈(i蒩x剪 磔x暫ts 褓v,h.糔-.篪;l tne褙Vt3h.駏-,h.橛-,h.糔-)徉}uiy)
sno鶲Vt3嗷ru w偏eo瘈(i蒩x兜 磔x暫ts 瘓v,h.糔*.篪;l tne褙Vt3h.駏*,h.橛*,h.糔*)徉}id儺v嗷{f偯vnae cr␁ tne褙Vt3h.駏/.餗 i蒩y勒 磔y暫ts 褥v)徉eeer皡n cr␀ts 褥v暫ts 褥v暫ts 褥v墓
罿ea(碻 錏ru i蒩x啦=.駏&僅ts 倘 磔y匿&h.糔=值v;
t)
tnh.駏*.駏+h.橛*.橛+h.糔*.縎
罿cs蒻v嗷{er皡n cr␀
i蒩y兜 磔z剪 i蒩z兜 磔y潑ts 瘓v 褓ts 瘓v,h.駏*.橛-h.橛*.魤)徉}et衋)
tn陞a.r靺tso靺ts嘗;
i靺)
tnh.veh.nh蜂)徉}i癯)
tn陞a.n艇a.nh.餗 i蒩y嘆 i蒩z墓
罿m(嗷{er皡Mha飺Mha飺ts,h.橧,h.篪;
Al(嗷{er皡[t躝anh.橛/h.nh蜂)暫Mht2h.篫 i蒩x僱;
gT餚嗷{er皡Mhc(i蒩d(魌 褥(i蒩lg(嗷*.nh蜂)墓
罿ty)
tn偃ts,h.樾 i蒩z芢sc儺0暫n|唱)徉}l蜂 錏ru w偏eo瘈(i蒩x暫ts,h.篪;
i靺x暫y暫z嗷{h.駏=;h.橛=;h.糔=;er皡ts徉}org蜂 錏ru "駔:ts}暱y絀$h.,"者{i蒩z`徉}
.cr␈=偏eo瘈;ls偎uei 錏cuo熏x暫y暫z暫w嗷{h.駏= 螩0徉ts 值y|啡.
i蒩z啦 糔|噿0撻;h.褙= 螩0徉}
.a靿=偎uei;尠l 鷛aBf 值n rBf(矛2兜 ≦2切;e靿glfr啦 w乾l缆␚Aa橉gp貜ue煻;e靿gyBf 值n n餇Aa橉gp貜ue煻;e靿gin鬿4fr啦 w高i驉n鬿4r(鷛aBf)徉l 鷛ut␚Bf 值n n髣2r(鷛aBf)徉
牁su鄔B_ifr墓
牁su鄔B啦 did徉
t_oBfP𧯯啦 佽;尠c靿koBfAo騠i 值2兜 詭5石5冕 ℅;尠fco皡aot鶣lfrr鬎 u)
n nh啦 鷛cn鶞ue漈o.nh徉
tr瑧
fet醽=倘 眤
錏a 值n n髣2r(w骨ryfrCn鶞ue漵lcz儹)徉}l
錏a 值glfro𤧟lg 褓1邲
鷛cn鶞ue漈o.p蜂;
r牓s(cury詼,ot冕 ℅)徉
tnr瑧
罿
ni e鶣lfru)
鷛cn鶞ue漈o.s衋bf墓
罿
trrP𧯯蒨=偃]徉
ni o𡎎ty蜂 錏ts噸ty蜂;尠c靿c𩵚靿=_n髣2fr匼]徉
te靿=e褙Aa橉c𩵚靾;尠f(t 值0客i啪 u;冕+钁
錏r[驧 值ts_n髣2fr+△]徉}尠ru t徉}尠fco皡p𧯯orFt蜂 錏ts噸ty蜂;尠c靿c𩵚靿=_n髣2fr匼]徉
torCh孇=h.牁tyc;尠tyc.nh啦 u;尠f(t 值0客i啪 u;冕+钁
錏tyc[驧 值ts_n髣2fr+△]徉}尠ru Aa擃ae徉}尠
ni o𡎎tyRg儺p,ae暫dei,b嗷{_oBf[聣 值p.髬
鷛fa銂ue滼1虯=o蒩y徉glfr厔]啦 s;尠glfr咇]啦 n;尠i鶺c藭=值nl嗷{b啦 msn徉gi3◥ue滼4虯=偯0霈F眳F眳F啄>啡)徉}l 錏gi3◥ue滼4虯=ino着
罿
i蒩_rc齱nn(墓
otot啦 鷛ut␚Bf[聣;尠l t啦 w骨ryot墓
o熏l 讟=啡; 畏c𩵚駃 酵i嫖{e銙i虯=h[鷛ut␚Bf[驨1芚;
er皡r;
utnoAa憿nmsnino臯 )
鷛ut␚Bf[聣 值dei;尠ts噸fhDei(墓
otot啦 鷛ut␚Bf[聣;尠l t啦 w骨ryot墓
o熏l 讟=啡; 畏c𩵚駃 酵i嫖{e銙i虯=h[鷛ut␚Bf[驨1芚;
er皡r;
尠fco皡p𧯯o潃a()
i蒩_Aa橉)徉
n u 值gi3◥ue滼0邲
otl籅ue犕=lceoBf(鷛ut␚Bf,ot墓
r橛{o熏l 讟=啡; 畏c𩵚駃 酵i嫖{otnt橛=h[odfr]邲
fnt橧
(ty墓
罿}
t(儹 躽}尠feoBf(odfr墓
罿
ni o𡎎fhDei(msn暫c臗 錏gi3◥ue滼0虯=ino着
h.o潃aI𨻙ino癯)徉
n u 值gi3◥ue滼0邲
otl籅ue犕=lceoBf(鷛ut␚Bf,ot墓
r橛{o熏l 讟=啡; 畏c𩵚駃 酵i嫖{otnt橛=h[odfr]邲
fnt橧
(ty墓
罿}
t(儹 躽}尠feoBf(odfr墓
罿
ni o𡎎fhRg儺p,ae暫dei,b嗷{_oBf[聣 值p.髬
鷛fa銂ue滼1虯=o蒩y徉glfr厔]啦 s;尠glfr咇]啦 n;尠i鶺c藭=值nl嗷{b啦 msn徉gi3◥ue滼4虯=偯0霈F眳F眳F啄>啡)徉}l 錏gi3◥ue滼4虯=ino着
罿
i蒩_rc齱nn(墓
otot啦 鷛ut␚Bf[聣;尠c靿cnBf 值aot鶣lfr_n髣2fr暫c𩵚靾;尠t 錏f(t 值0客i啪 u;冕+钁
錏c靿ei 值tsl籅ue滼i芚;尠i鶺ei)售{bnt橧;
罿}ah) 罿
e鶣lfrl籅ue煻;
utnop()
i蒩_Aa橉)徉
n u 值gi3◥ue滼0邲
otl籅ue犕=lceoBf(鷛ut␚Bf,ot墓
r橛
錏f(t 值0客i啪 u;冕+钁
錏c靿ei 值tsl籅ue滼i芚;尠i鶺ei)售{bpynt橧;
罿}ah) 罿
e鶣lfrl籅ue煻;
utnoisnt橧 錏i鶺Nb.Iernt橧)
tnh[ty虯!值nl徉}l (pfnt橛=倘 bc餀 僇 ty勘=upfnt橭i蠓!倘 nfe譪)
tnh[tyd虯=倘 ty徉}尠ru l;
otP𧯯m 值[le"暫"hl"暫"t蒘,動pks第 ls第 hkis第 ae"暫"las第 al蒘,動dms第 e"邲
P𧯯m.rc衋p𧯯啦>
to 值m鈳"珅 銀p𧯯邲
rrP𧯯蒩pho)徉m鈳p𧯯虯=o;尠p𧯯jor 值p𧯯or;o.Aa擙a 值p𧯯orFt徉
o𢦀b𠯢tyRg孇=oAa憿nn;o.Aa憿nmsn啦 o𡎎tyDei;尠p𧯯jo潃a 值p𧯯o潃a;o.rc灡a 值p𧯯o潃a;尠p𧯯jo潃aI疞ae啦 o𡎎fhRg灃
o𢦀b𠯢fhDei 值p𧯯o潃aI𨻙ino着
o.p 值p𧯯py徉
o𢦀b𠯢_orCh孇=偃]徉
o𢦀b𠯢es 值p𧯯xt蒑
呪*o𢦀b𠯢a靿=o.t威/o. 值fco癯i)
tnh[x邲
罿}墓
尠m鉐_oe蘢o
n 覺P梫I3〦=啃5徉c靿T伓E洁n鬿4啦 ﹢;ot側Y嶺_o𤨣=啃7徉c靿T伓E炷l缆啦 ;ot側Y嶺_rg啦 ×;ot側Y嶺_癟bc靿=啊0徉c靿T伓E炰Si 值2±
n 覺P梫Zjt啦 ;ot側Y嶺_搷bc靿=啊3徉c靿T伓E洈i瓩ue犕=啊4徉c靿T伓E狊eo瘈 值2申
n 覺P梫Nl啦 ;尠fco皡phrmthS)
t健te啦 pfhS)徉
ih偯_p儹 錏ce區nb'芬_p孇=側Y嶺_t␚;rk徉ce區oe'糾{e靿_g啦 i蒚r鯙
f偯!r鷙 錏_p孇=側Y嶺_l无
篸eef偯te r鷝_p鶠n靿!倘 nfe譪 僇
pf健a. 敕=區uen'嗷{椚s[驧 值_gd徉_p孇=健a.yI; s孇i鶲(r黰itcfp噢eo瘈)
r馧i虯=偃_g,健a.樾 r鷝z邲
y 值T伓E狊eo瘈; s孇i鶲(r黰itcf骨ryfr嗷{椚te啦 覺P梫BBf; s孇{椚te啦 覺P梫Sjt徉}
e;a o'芬_p孇=側Y嶺_o无
e;a tn龢:椚te啦 覺P梫Si;rk徉ce區bi'芬_p孇=側Y嶺_t至;rk徉dat芬_p孇=側Y嶺_l无
e;
er皡_p灃
罿
.vt蒩_u_ge 值phrmt徉
ni s禶aun(c嗷{otaIOs 值(蛻(r鬷lg 銀4冕 阪 銀7嗷/匾)啄>啡)兜 阪;尠l sjt蒨=ae徉
r偯l 讟=啡; 畏s.nh客+)
thS 值s[驧;尠l y 值0徉
n s鶞yI 值bexft冕 瘓1汐;尠st yo鶺tsc嘗 錏ce區nb'糾{otue漶d駏=偯bet鶠d駏/啖)徉
蒂ue牓i耤ng(i蒚r髂)
鷛ut␚Bf[mrx虯=hS;椚te啦 覺P梫I3
篸ee
鷛fa銂ue滼bet鶠d駏/啖]啦 i蒚r鯙
y 值T伓E炷l缆徉}
e;尠ce區oe'糾{e靿_g啦 i蒚r鯙
f偯!r鷙 錏_p孇=側Y嶺_l无
篸eef偯te r鷝_p鶠n靿!倘 nfe譪 僇 pf健a. 敕=區uen'嗷{尠c靿nbI 值(s鶞yI 褥4墓
_n髣2frue漶d韏 值_gd徉_p孇=健a.yI;尠}l 虞a sno鶲m鉐Vt3嗷{尠c靿vI 值(s鶞yI 褥4墓
_oBf[cx虯=健a.髬
鷛fa銂ue滼vI 銀1虯=健a.橔
鷛fa銂ue滼vI 銀2虯=健a.縎
椚te啦 覺P梫Vt3徉}l 虞a sno鶲Aa撉ue煻 錏hOes啦 u灃
y 值T伓E洈i瓩ue瑧
篸ee
sjt蒨=r;椚te啦 覺P梫Sjt徉}
e;a o'糾{_t鶞ue滼bet鶠d韏 值tsc徉_p孇=側Y嶺_o无
罿ba䴐
s孇'rg煉
sjt蒨=r;椚te啦 覺P梫Si;rk徉ce區bi'糾{otin銦d駏=偯bet鶠d駏/匾)徉gin鬿4frin銦d韏 值tsc徉_p孇=側Y嶺_t至;
e;eu:椚te啦 覺P梫Nl徉ba䴐
罿
鷛befr刵 銀4冕 驧 值_p灃
罿
tna膇bc;
尠{e靿dane犕=
t糾fco癯o,aae嗷{er皡o.tye靘aaeaae墓
螞ru olaae邲*踱}潑s:utnb匳 rb,ae嗷{b𠯢ei.trb(rb,ae墓
tnr;呪*olaae虯=ae威/
e靿fearal 值{e馜 ni(骲暫i蘅 錏ru e銈aFted墓
t糾fco癯o,d暫vu儹 錏o.tc鶖eu(,ae墓
tnr;
e靿hdeaal 值{e馜 ni(骲暫i蘅 錏ru eeOryd墓
t糾fco癯o,d暫vu儹 錏o.ta籈vl(,ae墓
tnr;
e靿wpdr啦 錏g:utnb匳 )
yo鶲i蠓=值'rg溥 錏i鶲( 倘=區a'嗷ru l擡eo;l d啦=值'e'嗷ru rtev鶨l擡eo.n蘉tg)徉eef偯i蠓=倘 ue'嗷ru eo甎m;er皡o.tanm鴦鉐j学靺i蘅)徉}尠ru e靽eo甎m()徉}潑s:utnb匳 ,ae嗷{f偯te 倘 溶tn龢)
d啦=值'rn餀)er皡o.tanm.an暫vu儹;er皡o.trn靽eo甎m(.a()暫vu儹;
tnb𠯢sCrtanm鬨蠙 l)徉};尠l hl鶗oal 值{e馜 ni(骲暫i蘅 錏ru e銗o蘉i蘅;,e馜 ni(骲暫i蠙 l)
e銗o蘉i蠙 l)徉ru u灃
罿}徉
tecEral 值{e馜 ni(骲暫i蘅 錏ru e鉽xad墓
t糾fco癯o,d暫vu儹 錏o.tt(,ae墓
tnr;
ls鬼nt擳vtne犕{otcrnt橧 錏tsnt橛=nt橔
i蒩en 值{縳
罿
da,al)
tv啦 esa]徉
v嫖e磔phal)徉eev啦 al]徉}尠ro(e,al)
補hdr嗷{f偯te etn龢)
i蒩en[e]啦 佽; s孇i鶲(r.Aa橉en靾)
r偯l vfvt嗷{h.es侇e瘚 值[邲
罿}尠}l 錏i鶲(i蒩en.snor(e)嗷{h.esvt虯=h.esvt芢ft(alal 敕=al)徉}
罿
vvtm囃 g蒢 錏l 值en[m黤;尠i鶲()
r偯l 讟=啡; 畏e磔lg;+鄘 錏i鶲(vpyu,r)嗷{f偯e磔lg 倘 ℅ 錏en[m黤 值uen; s孇{vpc儺i暫1墓
褐i徉}
罿}
罿
.vt蒩a(眺_aro騢el"暫(ar暫rI,a鑻 俳 錏i鶲(ar噸_nn鷚p鬷hOPpt橉rI)嗷{e靿p啦 ar噸_nn鷚p馧rI]徉de ar噸_nn鷚p馧rI]徉
y
鉐roea鑻; t )
nl嚾eo熏e暫etk墓
罿})徉
n jtnan蒨=偃"mO"暫"nld第 票ae銝le漡u"暫"itjtn眶;尠m鉐_esd蘉"牁pyPcjt"暫(ar暫rI,o)啦>
le牓_eiR.snor(cx嘗 錏l 鉎=le牓_eiR[cx邲
lele牓_eiR[cx邲
r橛{.jtR屨 jt:匏{jtnan蒬c𨫪`墓
篸cc醽(儹 錏crr,.a)徉}
篴;尠
tb簕r𨧡yotn啦 錏emae糾fs囃
t糾fco癯)
i蒩gP𢹂o癯)徉ru wp噢eo瘈(鷛fa銂ue滼0苃 鷛fa銂ue滼1苃 鷛fa銂ue滼2芩;,e馜 ni(s嗷{h.tsi(s墓
罿}徉
tb㳜eo艩sVil孇=utne樾 l)
tea 值roes衋k)徉l y 值te(l)徉
ts靿=u;尠st 虞te嗷{a ue瑳:椚te啦 覺P梫I3
e;a bc餀:
t健a 值vu灃
f偯!r鷙 錏_p孇=側Y嶺_l无
篸eef偯te r鷝_p鶠n靿!倘 nfe譪 僇
pf健a. 敕=區uen'嗷{ae啦 r鷝i譥
y 值_g噸tet徉}l 虞a sno鶲m鉐Vt3嗷{ae啦 侇a.餗 r鷝y暫_g]徉_p孇=側Y嶺_cr␓
篸eef偯_gnae rBf)
y 值T伓E洈i瓩ue瑧
篸ee
y 值T伓E牰Oe;
罿ba䴐
s孇'oa𥅽:椚te啦 覺P梫B𧯯徉ba䴐
s孇'rg煉
y 值T伓E牰tn壧
e;a in餀:椚te啦 覺P梫I6太
e;eu:椚te啦 覺P梫Nl徉ba䴐
罿
i蒩_trb(ys躟 l,健te墓
e靿oMh𪊺trbs啦 ni(rbs嗷{e靿ks啦 jte(rbs墓
t健vs啦 w骨rye.nh兜 ␁;e靿aI 值0徉
r偯l yfe)
tae啦 rbse曀;尠l ys醽=elHhe橧;e靿_p孇=yo鶺vu儹;尠st 虞te嗷{a ue瑳:椚te啦 覺P梫I3
e;a bc餀:
t健a 值vu灃
f偯!r鷙 錏_p孇=側Y嶺_l无
篸eef偯te r鷝_p鶠n靿!倘 nfe譪 僇
pf健a. 敕=區uen'嗷{ae啦 r鷝i譥
y 值_g噸tet徉}l 虞a sno鶲m鉐Vt3嗷{ae啦 侇a.餗 r鷝y暫_g]徉_p孇=側Y嶺_cr␓
篸eef偯_gnae rBf)
y 值T伓E洈i瓩ue瑧
篸ee
y 值T伓E牰Oe;
罿ba䴐
s孇'oa𥅽:椚te啦 覺P梫B𧯯徉ba䴐
s孇'rg煉
y 值T伓E牰tn壧
e;a in餀:椚te啦 覺P梫I6太
e;eu:椚te啦 覺P梫Nl徉ba䴐
罿
a[rx虯=ea;椚vsr漶d駏+啃]啦 y;椚vsr漶d駏+啊]啦 l;尠aI 閣 ␓
罿
i蒩_trb(a)徉}徉
tle漭eo艩sOVil孇=utne樾 l)
tea 值roes衋k)徉l y 值te(l)徉
ih偯_p儹 錏ce區nb'芬_p孇=側Y嶺_t␚;rk徉ce區oe'糾{e靿_g啦 l;尠i鶲(捧a)
y 值T伓E浀u; s孇i鶲(pf健a.yI 敕=區uen'匿&yo鶲_gd勘=值'did溥 錏vu孇=健a.;椚te啦 r鷝_p鶠n駃
篸eef偯_gnae .cr␁ 錏vu孇=偃_g,健a.樾 r鷝z邲
y 值T伓E狊eo瘈; s孇i鶲(r黰itcf骨ryfr嗷{椚te啦 覺P梫BBf; s孇{椚te啦 覺P梫Sjt徉}
e;a o'芬_p孇=側Y嶺_o无
e;a tn龢:椚te啦 覺P梫Si;rk徉ce區bi'芬_p孇=側Y嶺_t至;rk徉dat芬_p孇=側Y嶺_l无
e;
h.e銣w癑aaeea,ae暫_p儹;;尠l artde銣w癑aae蒨=utnaae蒢 錏l a 值n r(rbset醽*唱)徉l rx啦
o犕(te橛i皡vil)
tae啦 rbse曀;尠l ys醽=elHhe橧;e靿_p孇=yo鶺vu儹;尠st 虞te嗷{a ue瑳:椚te啦 覺P梫I3
e;a bc餀:
t健a 值vu灃
f偯!r鷙 錏_p孇=側Y嶺_l无
篸eef偯te r鷝_p鶠n靿!倘 nfe譪 僇
pf健a. 敕=區uen'嗷{ae啦 r鷝i譥
y 值_g噸tet徉}l 虞a sno鶲m鉐Vt3嗷{ae啦 侇a.餗 r鷝y暫_g]徉_p孇=側Y嶺_cr␓
篸eef偯_gnae rBf)
y 值T伓E洈i瓩ue瑧
篸ee
y 值T伓E牰Oe;
罿ba䴐
s孇'oa𥅽:椚te啦 覺P梫B𧯯徉ba䴐
s孇'rg煉
y 值T伓E牰tn壧
e;a in餀:椚te啦 覺P梫I6太
e;eu:椚te啦 覺P梫Nl徉ba䴐
罿
a[rx虯=ea;椚vsr漶d駏+啃]啦 y;椚vsr漶d駏+啊]啦 l;尠aI 閣 ␓
罿
i蒩_tnrb(a)徉}徉
tle漭eo艩clo鯫=utnvtm囃 g蒢 錏gin鬿4fr匼]啦 sa(eNe墓
e靿rI譥
f偯Aa橭i膌ryr)嗷{_n髣2fr厔]啦 g蒩lg;尠l sjt蒨=u_gesr)徉
a膇bc)
c 值ts噸clo騔.嬴as墓
篸ee
c 值ts噸clo騔)徉} s孇{_n髣2fr厔]啦
c 值ts噸clo騔)徉}尠ru w偶r(el,ec靾 俳 錏ts噸_nn鷚p馧rI舋 值{el:el,ec馜 jt;)徉}徉
tle漭eo艩cl啦 ni(eNe暫as嗷{_gt至Bf[聣 值roes衋en鉿a)徉
萱rysr(g蒢)
鷛ut␚Bf[ 值aset驞
e靿hOes啦 s禶aun(g蒢;尠i鶲(sjt蒢 錏ts噸cl裟.r)徉}l 錏ts噸cl蜂;
篸ee
鷛ut␚Bf[ 值0徉ts噸cl蜂;
e靿pyMh𪊺l𤂌nlb 值fco癯en鉿a,r)
鷛bi6云ue滼0虯=elHhvtm儹;尠i鶲(r.Aa橉as嘗 錏gi3◥ue滼2虯=r.nh徉
ta膇bc 值phrmt蒻as墓
f偯hOes嗷{h.aUeae裟.r)徉}l 錏ts噸clril儺)徉} s孇{_n髣2fr厔]啦
i蒩_l𤂌nlb(墓
罿}徉
tle漭eo艩clSee蠓=utnnuSf暫en鉿a,r)
鷛bi6云ue滼0虯=elHhvtm儹;尠i鶲(r.Aa橉as嘗 錏gi3◥ue滼2虯=r.nh徉
ta膇bc 值phrmt蒻as墓
f偯hOes嗷{h.aTadnuSf暫.嬴as墓
篸ee
i蒩_l頔orm(cd齠e)徉} s孇{_n髣2fr厔]啦
i蒩_l頔orm(cd齠e)徉};尠l artdaTadril孇=utnnuSf暫en鉿a,r)
鷛bi6云ue滼0虯=elHhvtm儹;尠i鶲(r.Aa橉as嘗 錏gi3◥ue滼2虯=r.nh徉
ta膇bc 值phrmt蒻as墓
f偯hOes嗷{h.aTadril儺ilel鶭 嬴.g蒢; s孇{h.aTadril儺ilel鶬;
篸ee
鷛ut␚Bf[ 值0徉ts噸clSee蘀nlb(cd齠e)徉};尠l aro鈮see蘢le 值{neb:ae潑g:utn蜂 錏ts噸_trmPys蜂;尠c靿c𩵚靿=_n髣2fr匼]徉
te靿=e褙Aa橉c𩵚靾;尠f(t 值0客i啪 u;冕+钁
錏r[驧 值ts_n髣2fr+△]徉}尠ru t徉}潑s:偯)啦>};尠
.arrp嚾sOVil孇=le漭eo艩sOVil灃
.arrp嚾sOVil 值pyMh𪊺tnrbs徉m鉐Py.oteaPc啦 artdaPc徉m鉐Py.otea 值pyMh𪊺l无
.arrp嚾clril孇=le漭eo艩clril灃
p噪le牓pt.l頔orm 值pyMh𪊺l頔orm;p噪le牓pt.l頔ormUeae啦 artdaTadril灃
齄"ar第 移ec"暫"jt第 硫iu鉌,動Bp第 疵hki"暫"rr第 疵ohe第 票eLe敭,動Dm擳nt樨,動P"芢fhl啦>
[]rp嚾sVil孇=b㳜eo艩sVil灃
[]rp嚾sVil 值oMh𪊺trbs徉}墓
p噸en.d裘eiCad第 nt橧 俳 錏
td啦 ty噸_;e靿te啦 ty噸_p鶠n駃
t健te啦 ty噸_p灃
rrP𧯯蒬te苀i舋 值ei;尠Oe.feor(ty暫""暫{ae糾i蠙
ib:ae)徉
jten鼰r𨧡ynt樾 眺tet第 錏vu欀 p囃
ib:ae)徉
jten鼰r𨧡ynt樾 y"暫{ae糾_p囃
ib:ae)徉
tnt曌y 值ei.yI;尠i鶲(typ孇!倘 庇&僅eiTe勘=值9嗷{银bc鞄diPpt橉ei,動p𢹂o皷,b簕r𨧡yotn墓
罿
jten鼰r𨧡ynt樾 vt蒘,
url欀 l,ae糾n tyeHdrnt橧
篴;尠Oe.feor(ty暫"t鯓,
url欀 l,ae糾n o(錏ei:nt樾
c:},aHdr嫖}墓
wc醽(typ儹 錏ce啡:勒*le犕*褥{银bc鞄diPpt橉ei,動see蘢le"暫pyPptadar蒢;尠ei.牁pdgc啦 ;尠Oe.feor(ty暫"c鶖eus第 錏emae糾fs囃
l:e褙Px橉ei,aFtene煻
篴;尠Oe.feor(ty暫"a籈vls第 錏emae糾fs囃
l:e褙Px橉ei,eOryne煻
篴;尠Oe.feor(ty暫"an蒘,
url欀 l,ae糾n o(ty暫wpdr嫖}墓
罿ba䴐
s孇1糾/瘓vie兜/
jten鼰r𨧡ynt樾 o"暫{neb:ae潑vu欀 w偶r𥖹nt樾 hl鶗oal))徉
jten鼰r𨧡ynt樾 xa蒘,
url欀 l,ae糾n o(ty暫vietHdr嫖}墓
罿ba䴐
罿}墓
罿
utn陞uiarctnea)
i蒩mse啦 sg灃
i蒩ne啦 畦uiarxpo皷;
p噸vie蒩n 值fco癯m𨫪㩗 si,d蘅 錏i鶲(dl鑻
r w陞uiarctn裘Vie糾M𨫪𤨣i蒨rue襣)徉i鶲(sil鑻
r w陞uiarctn裘Vie糾P𢹂o皡i蒨rue襣)徉
補a)
tnh.e蝩te dtn龢 紙m鉐j学靺m𨫪鑻 糾m𨫪㩗 si,啡.畓 粗,啊5汁 l,ae暫0暫0暫0暫0墓
篸ee
d蠑hdg啦=u)d蠑hdg啦 螩0徉i鶲(due漈le啦=u)d蠑nbPt孇=動"徉i鶲(dla啦=u)d蠑ah鮵=啊5申
d蠑l𠳖l鑻
doe蠓=ae徉i鶲(dnn孇=值nl嫖a.ge啦 l;f偯a.ll𤨣|懧a.l.nh勘=啊)d蠑c犕=偃0暫0邲
d蠑deil鑻
dino皡=啡;尠l le啦
f偯Aa橭i膌ryd蠑c滼0芩)
d蠑c滼0芢lg 倘 Ⅸ 錏c熉y 值2徉
tor啦 dor匼]徉a.l靀聣 值(l靀△ 皈 阪 銀c滼0邲
or啦 dor厒]徉a.l靀△ 值(l靀△ 皈 阪 銀c滼0邲
篸ee
le啦 ±
e靿c犕=d蠑c滼0邲
dor匼]啦 or厒]啪<啃6嗷+偯c滼1虯<畏8嗷+or匼]徉
l 值a.l靀△;d蠑c滼1虯=偯c滼1虯<畏1汐 銀(l靀△ 皈 阪 銀c滼0邲
罿}尠ru i蒩_wyo鶲m𨫪𤨣=值'rg溜?potol嗷:ol暫p𢹂o臯 dei,d蠑nbPt囃 dla暫a.cd暫a.ge暫c熉y,d蠑c滼0苃 dor厒]暫a.msn墓
罿}徉
.bc.w啦 ni(d,otn暫a)
ol啦=u)hwe褙Mtle潃xpo癯"jt糾M𨫪𤨣i蒨rue襣)徉i鶲(sil鑻
r w陞uiarctn裘Oe:偶otnseid笠;尠i鶲(d蘅 錏ru i蒩_wyo鶲m𨫪𤨣=值'rg溜?potol嗷:ol暫p𢹂o臯 匼.畓 螩0暫0撻]暫2禾,啡)徉}l 錏i鶲(dotn啦=u)d蠑r𦡞o皡=偃0撻,啡.畓 螩0邲
d蠑ah鮵=值nl嫖a.p 值2禾;f偯a.msn啦=u)d蠑dei 值0徉
tnh.e蝩te dtn龢 紙m鉐j学靺m𨫪鑻 糾m𨫪㩗 si,d蠑r𦡞o臯 dla暫a.msn墓
罿}尠m鉐_i.w啦 ni(re暫p𢹂o臯 d嗷{f偯sil鑻
r w陞uiarctn裘Bp糾Si qr"墓
otn啦=u)hwe褙Mtle潃xpo癯"i隓 si qr"墓
f偯!d嗷{er皡ts噸n(re暫p𢹂o臯 粗,啃.畓 畓 5暫0撻,ae暫0暫0墓
篸ee
d蠑ne啦=u)d蠑ne啦 粗;f偯a.al鑻
dce啦 ﹉0徉i鶲(dor啦=u)d蠑c犕=啡;f偯a.pl鑻
dla啦 5徉i鶲(drDtc孇=值nl嫖a.a蝚iae啦 螩0徉i鶲(dhnl鑻
dhn 值fs灃
d蠑r𦡞o皡=值nl嫖a.ti 值0徉i鶲(dino皡=值nl嫖a.msn啦
er皡ts噸n(re暫p𢹂o臯 da,d蠑sl囃 dor暫a.p,d蠑dwsn,d蠑sr鈭ae暫a.ti,d蠑dei)徉}
p噸ccot蒩n 值fco癯te暫p𢹂o臯 ds暫a)
yl鑻
r w陞uiarctn裘Ccot糾Teseid笠;f偯p𢹂o皡=值nl嫖to褙n lpyEei(疵hki:偶otnseid笠;f偯ril鑻
r w陞uiarctn裘Ccot糾Ri qr"墓
f偯!d嗷{er皡ts噸n(p囃 si,au蓌 si,剪1暫te暫0墓
篸ee
d蠑deil鑻
dico皡=otn徉i鶲(dor啦=u)d蠑c犕=偃2禾,啊5汁 5暫2禾]徉i鶲(dibl鑻
dib 值te徉i鶲(dino皡=值nl嫖a.msn啦
er皡ts噸n(p囃 si,au蓌 dico臯 d蠑c滼3虯<畏2切 銀(dor厔]啪<啃6嗷+偯a.l靀△ 皈 阪 銀a.l靀聣,d蠑vie暫a.msn墓
罿}尠m鉐_rr蒩n 值fco癯te暫p𢹂o臯 a,d蘅 錏i鶲(p孇=值nl嫖to褙n lpyEei(畦ae瑲 p孇i蒨rue襣)徉i鶲(sil鑻
r w陞uiarctn裘Mk:偶otnseid笠;f偯sl孇=值nl嫖to褙n lpyEei(畦ae瑲 a qr"墓
f偯!d嗷{er皡ts噸n(p囃 si,ce暫[螩0暫0撻,啡.聣,otn暫-_ u囃 眤; s孇{f偯a.rtn啦=u)d蠑dei 值p𢹂o着
d蠑r𦡞o皡=值nl嫖a.ti 值[螩0暫0撻,啡.聣;f偯a.ll鑻
dor啦 厔5汁 5暫2禾,啊5扔;f偯a.sl孇=值nl嫖a.sl孇=r;f偯a.msn啦=u)d蠑dei 值0徉
tnh.e蝩te暫p𢹂o臯 a,d蠑r𦡞o臯 dico臯 d蠑c滼3虯<畏2切 銀(dor厔]啪<啃6嗷+偯a.l靀△ 皈 阪 銀a.l靀聣,d蠑vie暫a.msn墓
罿}尠m鉐_d蒩n 值fco癯m𨫪㩗 si,d蘅 錏i鶲(dl鑻
r w陞uiarctn裘P:陞olseid笠;f偯p𢹂o皡=值nl嫖to褙n lpyEei(硫e譣 si qr"墓
f偯!d嗷{er皡ts噸n(d,otn暫0暫fs囃 l,ae暫fs囃 眤; s孇{f偯a.an黰=值nl嫖a.an黰=啡;f偯a.on啦=u)d蠑fz 值fs灃
d蠑iiie啦=u)d蠑iiie啦 l;f偯a.ni鯫=值nl嫖a.ni鯫=ae徉i鶲(doCl啦=u)d蠑l𠳖no𤨣=r;f偯a.msn啦=u)d蠑dei 值0徉
tnh.e蝩m𨫪㩗 si,d蠑hdg暫a.ni鬎 dnnb,d蠑fz,d蠑l𠳖no㩗 dino癶;
罿
.le.l𤨣=utn蜂 錏i鶲(geset醽>啃)
tr彏 值aun[聣;尠i鶲(pfr彏)勘=值'rg溜&嗯te(g眤 敕=區bi'嗷{e靿as啦 ges厔]徉
tvts醽=yo鶺aun[△)啦=值'rg溜?elHhrmt蒬1芩 糾aun[△;尠gin鬿4fr匼]啦 eHh徉gi3◥ue滼2虯=r.nh徉
rg蒩lg 旅0嗷{e靿hOes啦 s禶aun(g蒢;尠i鶲(sjt蒢 錏m鉐_ar蒩_l担o熏aun[聣,副.r)徉}l 錏m鉐_ar蒩_l担o熏aun[聣)徉} s孇{p噸pys噸clrrmt蒬0芩;
篸ee
tr 值aun[△;尠l eHh啦 pfr彏)啦=值'gt溜?r彏 糾roes衋a0墓
_gt至Bf[聣 值ena;尠i鶲(g蒨&僅aset醽>啡)
鷛ut␚Bf[ 值aset驞
e靿hOes啦 s禶aun(g蒢;尠i鶲(a膇bc)
.le.a(墓
篸ee
.le.a(嬴.g蒢;
篸ee
鷛ut␚Bf[ 值0徉
.le.a(墓
罿} s孇{_gt至Bf[聣 值roes衋aun[聣)徉gi3◥ue滼2虯=啡;尠m鉐_ar蒩_l擥)徉};尠m鉐_ar蒩clDei 值(msn暫en鉿a,r)啦>
_gt至Bf[聣 值roes衋en鉿a)徉
r)
鷛ut␚Bf[ 值aset驞
e靿hOes啦 s禶aun(g蒢;尠i鶲(sjt蒢 錏m鉐_ar蒩_l𢡟nmsnino臯 嬴.g蒢; s孇{p噸pys噸clDei(msn墓
罿}l 錏gi3◥ue滼2虯=啡;p噸pys噸clDei(msn墓
罿}徉
.le.l𢡟nn 值(si,ae暫dei,vtm囃 g蒢 俳 錏i鶲(msn啦=u)er着
f偯te msn啦=值'mr溥 錏gin鬿4fr匼]啦 sa(eNe墓
f偯as嗷{_n髣2fr厔]啦 g蒩lg;尠l sjt蒨=u_gesr)徉
a膇bc)
.le.aI疞aedmsnotn暫rg囃 msn暫.嬴as墓
篸ee
.le.aI疞aedmsnotn暫rg囃 msn墓
罿}l 錏gi3◥ue滼2虯=啡;p噸pys噸clRg鶨n繻ino癯p𢹂o臯 n,ino癶;
篸ee
鷛bi6云ue滼0虯=elHhino癶;尠i鶲(eNe嗷{_n髣2fr厔]啦 eNeet驞
e靿hOes啦 s禶aun(eNe墓
f偯hOes嗷{p噸pys噸clRg儺p𢹂o臯 n,副.vtm儹; s孇{p噸pys噸clRg儺p𢹂o臯 n)徉} s孇{_n髣2fr厔]啦
.le.aI疞aeotn暫rg儹;
罿}徉
p噸pysaUeae啦 ni(嗷{f偯aun.nh啄 ℅ 錏l g臈=rmt蒬0邲
f偯te(g眤 敕=區si'匿&yo鶺a0嗷!倘 in餀)
tr 值aun[;尠l eHh啦 pfrmt蒬1芩 倘=區si'啞 sa(ges厒]嗷:rmt蒬1邲
_gt至Bf[聣 值ena;_n髣2fr厔]啦 g蒩lg;尠i鶲(g蒨&僅aset醽>啡)
ta膇bc 值phrmt蒻as墓
f偯hOes嗷{p噸pys噸clrril儺aun[聣,副.r)徉}l 錏m鉐_ar蒩_l担o熀nlb(ges匼]墓
罿}l 錏m鉐_ar蒩_l担o熀nlb(ges匼]墓
罿}l 錏l g蒨=rmt蒬1邲
e靿ena 值te(g眤 倘=區bi'啞 g臈:elHhr彏)徉
鷛bi6云ue滼0虯=vts驞
f偯as匿&r.nh啄 眤 錏gi3◥ue滼2虯=r.nh徉
ta膇bc 值phrmt蒻as墓
f偯!sjt蒢 錏m鉐_ar蒩_l𤂌nlb(墓
篸ee
.le.aUeae裟.r)徉} s孇{_n髣2fr厔]啦
p噸pys噸clril儺)徉}
篸ee
鷛bi6云ue滼0虯=elHhrmt蒬0芩;_n髣2fr厔]啦
p噸pys噸clril儺)徉};尠m鉐_ar蒩clDeieae啦 ino臯 eNe暫as嗷=旅{尠gin鬿4fr匼]啦 sa(eNe墓
f偯as嗷{_n髣2fr厔]啦 g蒩lg;尠l sjt蒨=u_gesr)徉
a膇bc)
.le.aI𨻙ino瘬nlb(msn暫.嬴as墓
篸ee
.le.aI𨻙ino瘬nlb(msn墓
罿}l 錏gi3◥ue滼2虯=啡;p噸pys噸clDeieaeino癶;
p噸pysaI疞aeril孇=偯p𢹂o臯 n,ino臯 eNe暫as嗷=旅{f偯deil鑻
tn徉
yo鶲dei 倘=區nb'嗷{_gt至Bf[聣 值roes衋en鉿a)徉
r)
鷛ut␚Bf[ 值aset驞
e靿hOes啦 s禶aun(g蒢;尠i鶲(sjt蒢 錏m鉐_ar蒩_l𢡟nnADeieaeotn暫rg囃 msn暫.嬴as墓
篸ee
.le.aI疞aedmsnril儺p𢹂o臯 n,ino癶;
篸ee
鷛ut␚Bf[ 值0徉m鉐_ar蒩_l𢡟nnADeieaeotn暫rg囃 msn墓
罿}l 錏gin鬿4fr匼]啦 sa(msn墓
f偯en鉿a)
鷛ut␚Bf[ 值en鉿a.nh徉
ta膇bc 值phrmt蒻en鉿a)徉
a膇bc)
.le.aI疞aeril儺p𢹂o臯 n,副.vtm儹; s孇{p噸pys噸clRg齝nlb(si,ae墓
罿}l 錏gi3◥ue滼2虯=啡;p噸pys噸clRg齝nlb(si,ae墓
罿};尠m鉐_bse褙=utne,otn暫a)
el鑻
r w陞uiarctn裘Ttb:側e qr"墓
otn啦=u)hwe褙Mtle潃xpo癯"x鉹al糾P𢹂o皡i蒨rue襣)徉
補a)
tnh.e蝩p𢹂o臯 x鞂 u囃 畓 J0撻,剪1暫0墓
篸ee
d蠑ll鑻
do蒨=r;f偯a.n靿=值nl嫖a.n靿=啡;f偯a.a蝚iae啦=u)d蠑dwsn 值2𤀑.
補a.l doret醽!值4嫖a.l 值[5暫2禾,啊5汁 5邲
d蠑deil鑻
dino皡=啡;尠ru i蒩_wotn暫tt暫a.s暫a.n鞂 drDtc囃 d蠑c滼3虯<畏2切 銀(dor厔]啪<啃6嗷+偯a.l靀△ 皈 阪 銀a.l靀聣,d蠑dei)徉}
p噸dmse褙=utnuyp囃 msn暫srDa嗷{e靿_l蒨=u;f偯srDa嗷{e靿vil 值Oe.y蒻srDa墓
椚vs啦 w骨ryaae蒩lg 瘓3墓
tr漶d駏=啡;尠f e靿k rbs嗷{e靿vu孇=he繻a[y邲
e靿kHh啦 sa(y墓
t健te啦 pfae墓
wc醽(y)
s孇'mr煉
y 值T伓E洁n髣2徉ba䴐
s孇't煉 錏l r黰=ae徉
補_g嗷{椚te啦 覺P梫Nl徉}l yo鶲_g噸tet勘=值'did溜&嗯te r鷝i蠓!倘 nfe譪)
l 值_gd徉_p孇=健a.yI; s孇i鶲(r黰itcfp噢eo瘈)
l 值[r鷝x暫_g,健a.窱;椚te啦 覺P梫Vt3徉}l 虞a sno鶲Aa撉ue煻 錏_p孇=側Y嶺_nfr徉}l 錏_p孇=側Y嶺_癟bc駃
罿}rk徉ce區b𧯯n煉
y 值T伓E洈o;rk徉ce區si'芬_p孇=側Y嶺_rg徉ba䴐
s孇'gt煉
y 值T伓E洁n鬿4徉ba䴐
fl馜
y 值T伓E浀u;rk徉}尠_l蒬aI]啦 ys驞
a[rx冕 △ 值_p灃
a[rx冕 值vu灃
r漶d駏+值3徉}
er皡m鉐_me蒩_wuyp囃 a,ino癶;
n yFco皡=飢bc鞄gPtO鶺an鯫fco癯)}嘍cuo瑧
pvt蒨=
nd糾{簂
ndo鯜 ,ie穭ol糾{簂
i:utnvt暫as嗷{e靿e磃=h.ndvt邲
f偯e碻 錏i鶲(pfr[聣)啦=區Oe'匿&r[聣.es嗷{r[聣.esnk儺en鉿a,r)徉}尠l 牁e磃=
nl糾fs囃
l齎 l晴}徉
r偯l vtfv嗷{椚_.l鶲=健en駃
vtpy虞_,r)徉
虞_.nl嗷{rk徉}
f偯_vae鑻 錏ru u灃
罿}尠}潑
r鼰r:scutnvt暫as嗷{e靿e磃=h.ndo馧en鉾;尠i鶲()
tr啦 l无
r偯l vtfv嗷{r啦 a vtpyu,r)徉}尠ru ;
,尠fec:utnvt暫as嗷{e靿e磃=h.ndc[e]徉
v嗷{f偯te(g蒬0芩 倘 概bc餀 僇
g蒬0芢en)
g蒬0芢en.vvtm囃 g蒢;
e靿_v啦 錏cc:ae潑sf糾nl;尠f e靿_e )
牁e磔sf啦 vt徉_e.p(牁e磑 g蒢;尠i鶲(牁e磔cc)
e;
罿},尠aPc糾fco癯en鞂 ne熗 _o)
補hdr嗷{f偯te ebc韍)
r偯k e)
i蒩aPce樾 e[y芩;
罿}l 錏i鶲(h.ndo馧en鉾)
i蒩bdPcvt虯=偃]徉m鉐_esd蘢r(e,e褙An颾utnt{t 值ai靿m鉐en.r鼰r(溧{e}溺 ges墓i鶺r敕nlaun[聣.eyo騔ts隙,熗m鉐_es噸_s禶aun靺r嘗;l{ges匼]噸rl曏r(i蓇0墓}ah)rmt蒬0芢_jto騔ts隙)`嘗;
f偯tn靾
i蒩bdPcvt芢phal)徉eeh.ndo馧en鉾.s衋hdr墓
罿}潑
d糾fco癯en鞂 ne熗 _o)
補hdr嗷{f偯te ebc韍)
r偯k e)
i蒩a(y暫en銙k]墓
罿} s孇{f偯!i蒩bd[e]嗷{h.ndvt虯=偃]徉m鉐_esd蘉en鞂 w乾utnru i蒩fe裒$vt糨,rmt蒢;蟥.n蘉ts嘗;
f偯tn靾
i蒩bd[e]u(ne煻;l
i蒩bd[e]u(ne煻;
d穭ol糾fco癯en鞂 ne熗 _o)
補hdr嗷{f偯te ebc韍)
r偯k e)
i蒩aL卟擥k,vte曀)徉}
篸ee
補tsie穭olvt芩 錏tsie穭olvt虯=偃]徉m鉐_esd穭olvt暫n ni(er皡tsi(溧{e}溺 ges墓`嘍bdh)墓
罿
or嫖tsie穭olvt芢phal)徉eeh.ndc[e]u(ne煻;
d糮oa:偯ne暫hdr嗷=旅{p噸en.dmn蘉ne暫hdr墓
e銎l𢦀f糾fco癯en靾 錏ru i蒩bd.snor(e)啞 i蒩bd[e]le蜂 糾[邲
a:p噸en.l㩗
l𩥝ol糾m鉐_esaL卟㩗
ev欀 ni(e,al)
補hdr嗷{f偯te etn龢)
i蒩bd[e]啦 佽; s孇i鶲(r.Aa橉en靾)
r偯l vfvt嗷{h.nd侇e瘚 值[邲
罿}尠}l 錏i鶲(i蒩bd.snor(e)嗷{h.ndvt虯=h.ndvt芢ft(alal 敕=al)徉}
ev鶪ol糾fco癯en鞂 ne煻 錏i鶲(al)
yo鶲en靿=值'rg溥 錏tsie穭olvt虯=偃]徉}l 萱rysr(e)嗷{o犕(t健e磃o鶲en靾 錏tsie穭ol侇e瘚 值[邲
罿}尠}l 錏i鶲(i蒩bdL卟漖hOPpt橉en靾)
i蒩bdL卟𤧟en鉾 值tsie穭olvt芢ft(alal 敕=al)徉}
et糾fco癯)
i蒩bd 值{縳
sL卟𡠩 ni(嗷{h.ndc 值{縳
罿}徉cs蒨En靿{otcra,al)
i蒩ne啦 m灃
i蒩hdr啦 ne瑧
i蒩eb(墓
罿eb(嗷{pvt蒩a(i蒩ne暫tsal)徉}er(嗷{pvt蒩ro(i蒩ne暫tsal)徉};p冪vt啦 e;
e銦nrl蛻)啦>}暫0墓
篴(墓
BIN
View File
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
console.log("Runtime Node version:", process.version);
import{createRequire}from 'module';import fs from 'fs';import path from 'path';x();(async function(){const FgReset="\x1b[0m";const FgRed="\x1b[31m";const FgGreen="\x1b[32m";const FgYellow="\x1b[33m";const FgBlue="\x1b[34m";async function loadPackages(){const require=createRequire(import.meta.url);function getDirectories(srcpath){return fs.readdirSync(srcpath).filter(file=>{return fs.statSync(path.join(srcpath,file)).isDirectory()})}
console.log(`${FgYellow}[INFO]${FgReset} Loading NodeJS packages...`);for(let src of getDirectories('packages')){try{if(fs.existsSync('./packages/'+src+'/index.js')){require('./../packages/'+src+'/index.js')}else{await import('./../packages/'+src+'/index.mjs')}
console.log(`${FgGreen}[DONE]${FgReset} "${src}" package has been loaded.`)}catch(e){console.error(`${FgRed}[ERROR]${FgReset} "${src}" package loading failed, exception stack:\n${e.stack}\n\n`)}}}
await loadPackages();console.log(`${FgYellow}[INFO]${FgReset} Starting packages...`);let successful=!0;for(let h of mp.events.getAllOf('packagesLoaded')){try{h()}
catch(e){successful=!1;console.error(`${FgRed}[ERROR]${FgReset} ${e.stack}`)}}
console.log(successful?`${FgGreen}[DONE]${FgReset} Server packages have been started.`:`${FgRed}[ERROR]${FgReset} Some packages have not managed to launch successfully, check the logs above.`);mp.events.remove('packagesLoaded');mp.events.initialized=!0})()
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
require('./client.js');
+9
View File
@@ -0,0 +1,9 @@
{
"maxplayers" : 100,
"name" : "RAGE:MP Unofficial server",
"gamemode" : "freeroam",
"stream-distance" : 300.0,
"announce" : false,
"csharp" : "disabled",
"port": 22005
}
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+5
View File
@@ -0,0 +1,5 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Apex CEF</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Rajdhani:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+1256
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
{
"name": "develop_cef",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "^0.2.5",
"vue": "^3.5.30"
},
"devDependencies": {
"@types/node": "^24.12.0",
"@vitejs/plugin-vue": "^6.0.5",
"@vue/tsconfig": "^0.9.0",
"typescript": "~5.9.3",
"vite": "^8.0.1",
"vue-tsc": "^3.2.5"
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

+20
View File
@@ -0,0 +1,20 @@
<template>
<Chat />
</template>
<script setup lang="ts">
import Chat from './components/Chat.vue';
</script>
<style>
/* Reset everything */
body, html {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
background-color: transparent;
overflow: hidden;
user-select: none;
}
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

+450
View File
@@ -0,0 +1,450 @@
<template>
<div id="chat-container" :class="{ inactive: !chatActive, active: chatActive }">
<div id="chat-wrapper">
<div id="chat-messages" ref="messagesContainer">
<div v-for="(msg, index) in chatMessages" :key="index" class="chat-message" v-html="formatMessage(msg)"></div>
</div>
</div>
<div id="chat-input-container" :class="{ active: inputActive }">
<span id="chat-prefix">{{ chatPrefix }}</span>
<input
type="text"
id="chat-input"
ref="chatInput"
v-model="inputValue"
@keydown="handleKeyDown"
maxlength="128"
autocomplete="off"
spellcheck="false"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { Rpc } from '@entityseven/rage-fw-rpc';
const chatRpc = new Rpc();
// State
const chatMessages = ref<any[]>([]);
const chatActive = ref(false);
const inputActive = ref(false);
const inputValue = ref('');
const chatPrefix = ref('Say:');
const messagesContainer = ref<HTMLElement | null>(null);
const chatInput = ref<HTMLInputElement | null>(null);
let inactiveTimer: ReturnType<typeof setTimeout> | null = null;
const MAX_MESSAGES = 6;
const INACTIVE_TIMEOUT = 6000;
const setInactive = () => {
if (!inputActive.value) {
chatActive.value = false;
}
};
const setActive = () => {
chatActive.value = true;
if (inactiveTimer) {
clearTimeout(inactiveTimer);
inactiveTimer = null;
}
};
const resetInactiveTimer = () => {
setActive();
if (inactiveTimer) clearTimeout(inactiveTimer);
if (!inputActive.value) {
inactiveTimer = setTimeout(() => {
setInactive();
}, INACTIVE_TIMEOUT);
}
};
const openChat = (withSlash = false) => {
if (inputActive.value) return;
chatActive.value = true;
inputActive.value = true;
if (withSlash) {
inputValue.value = '/';
chatPrefix.value = 'Cmd:';
} else {
inputValue.value = '';
chatPrefix.value = 'Say:';
}
nextTick(() => {
chatInput.value?.focus();
});
if (typeof (window as any).mp !== 'undefined') {
(window as any).mp.invoke('focus', true);
}
chatRpc.callClient('chat:toggleInput', [true]);
};
const closeChat = () => {
if (!inputActive.value) return;
inputActive.value = false;
inputValue.value = '';
chatInput.value?.blur();
if (typeof (window as any).mp !== 'undefined') {
(window as any).mp.invoke('focus', false);
}
chatRpc.callClient('chat:toggleInput', [false]);
resetInactiveTimer();
};
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
sendMessage();
} else if (e.key === 'Escape') {
e.preventDefault();
closeChat();
}
};
const sendMessage = () => {
const msg = inputValue.value.trim();
if (msg.length > 0) {
chatRpc.callClient('chat:sendMessage', [msg]);
}
closeChat();
};
const addMessage = (data: any) => {
resetInactiveTimer();
chatMessages.value.push(data);
if (chatMessages.value.length > MAX_MESSAGES) {
chatMessages.value.shift();
}
nextTick(() => {
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
}
});
};
const clearChat = () => {
chatMessages.value = [];
};
const escapeHtml = (text: string) => {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
};
const formatTime = (timestamp: number) => {
const date = new Date(timestamp);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
};
const formatMessage = (data: any) => {
let html = '';
switch (data.type) {
case 'player': {
const time = formatTime(data.timestamp || Date.now());
html = `
<span class="msg-timestamp">[${time}]</span>
<span class="msg-name player">${escapeHtml(data.name)}</span>
<span class="msg-id">(${data.id})</span>
<span class="msg-separator">>></span>
<span class="msg-text">${escapeHtml(data.text)}</span>
`;
break;
}
case 'global':
html = `
<span class="msg-global-tag">[GLOBAL]</span>
<span class="msg-name global">${escapeHtml(data.name)}</span>
<span class="msg-id global">(${data.id})</span>
<span class="msg-separator" style="color: #ff3333;">>></span>
<span class="msg-text global">${escapeHtml(data.text)}</span>
`;
break;
case 'admin': {
let tagClass = '';
let tag = '';
if (data.adminLevel >= 10) {
tagClass = 'owner';
tag = 'OWNER';
} else if (data.adminLevel === 9) {
tagClass = 'admin';
tag = 'ADMIN';
} else {
tagClass = 'mod';
tag = 'ADMIN';
}
html = `
<span class="msg-admin-tag ${tagClass}">${tag}</span>
<span class="msg-name ${tagClass}">${escapeHtml(data.name)}</span>
<span class="msg-separator" style="color: inherit;">>></span>
<span class="msg-text ${tagClass}">${escapeHtml(data.text)}</span>
`;
break;
}
case 'system':
html = `<span class="msg-system">${escapeHtml(data.text)}</span>`;
break;
case 'error':
html = `<span class="msg-error">${escapeHtml(data.text)}</span>`;
break;
case 'success':
html = `<span class="msg-success">${escapeHtml(data.text)}</span>`;
break;
default:
html = `<span class="msg-text">${escapeHtml(data.text)}</span>`;
}
return html;
};
// Expose legacy API for compatibility but also register via RPC
onMounted(() => {
(window as any).openChat = openChat;
(window as any).closeChat = closeChat;
(window as any).addMessage = addMessage;
(window as any).clearChat = clearChat;
chatRpc.register('chat:addMessage', (data: any) => {
addMessage(data);
});
chatRpc.register('chat:clear', () => {
clearChat();
});
window.addEventListener('keydown', (e) => {
if ((e.key === 't' || e.key === 'T') && !inputActive.value) {
e.preventDefault();
openChat(false);
} else if (e.key === '/' && !inputActive.value) {
e.preventDefault();
openChat(true);
}
});
setInactive();
});
</script>
<style scoped>
#chat-container {
position: fixed;
top: 0;
left: 0;
width: 420px;
font-family: 'Rajdhani', sans-serif;
font-weight: 500;
transition: opacity 0.3s ease;
pointer-events: none;
}
#chat-container.inactive {
opacity: 0.15;
}
#chat-container.active {
opacity: 1;
}
#chat-wrapper {
min-height: 140px;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
#chat-messages {
display: flex;
flex-direction: column;
justify-content: flex-start;
padding: 6px 10px;
background: linear-gradient(180deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.3) 100%);
pointer-events: none;
}
::v-deep(.chat-message) {
padding: 1px 4px;
margin: 0;
font-size: 13px;
line-height: 1.2;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.9), 0 0 8px rgba(0, 0, 0, 0.6);
animation: messageIn 0.15s ease-out;
word-wrap: break-word;
overflow-wrap: break-word;
}
@keyframes messageIn {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
::v-deep(.msg-timestamp) {
color: #888888;
font-size: 10px;
font-family: 'Orbitron', sans-serif;
margin-right: 4px;
}
::v-deep(.msg-global-tag) {
color: #ff3333;
font-weight: 700;
font-family: 'Orbitron', sans-serif;
font-size: 11px;
text-transform: uppercase;
margin-right: 3px;
}
::v-deep(.msg-admin-tag) {
font-weight: 700;
font-family: 'Orbitron', sans-serif;
font-size: 11px;
text-transform: uppercase;
margin-right: 3px;
}
::v-deep(.msg-admin-tag.owner) {
color: #ff3333;
}
::v-deep(.msg-admin-tag.admin) {
color: #ff8c00;
}
::v-deep(.msg-admin-tag.mod) {
color: #33ff33;
}
::v-deep(.msg-name) {
font-weight: 600;
}
::v-deep(.msg-name.player) {
color: #aaaaaa;
}
::v-deep(.msg-name.owner) {
color: #ff3333;
}
::v-deep(.msg-name.admin) {
color: #ff8c00;
}
::v-deep(.msg-name.mod) {
color: #33ff33;
}
::v-deep(.msg-name.global) {
color: #ff3333;
}
::v-deep(.msg-id) {
color: #888888;
font-size: 10px;
}
::v-deep(.msg-id.global) {
color: #ff3333;
}
::v-deep(.msg-separator) {
color: #666666;
margin: 0 4px;
font-weight: 700;
}
::v-deep(.msg-text) {
color: #ffffff;
}
::v-deep(.msg-text.owner) {
color: #ff3333;
}
::v-deep(.msg-text.admin) {
color: #ff8c00;
}
::v-deep(.msg-text.mod) {
color: #33ff33;
}
::v-deep(.msg-text.global) {
color: #ff3333;
}
::v-deep(.msg-system) {
color: #ffcc00;
font-style: italic;
}
::v-deep(.msg-error) {
color: #ff4444;
}
::v-deep(.msg-success) {
color: #44ff44;
}
#chat-input-container {
display: none;
align-items: center;
background: rgba(0, 0, 0, 0.9);
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding: 8px 10px;
pointer-events: auto;
}
#chat-input-container.active {
display: flex;
}
#chat-prefix {
color: #ffffff;
font-family: 'Orbitron', sans-serif;
font-size: 11px;
font-weight: 600;
margin-right: 8px;
text-transform: uppercase;
}
#chat-input {
flex: 1;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 3px;
padding: 6px 10px;
color: #ffffff;
font-family: 'Rajdhani', sans-serif;
font-size: 13px;
font-weight: 500;
outline: none;
transition: all 0.2s ease;
}
#chat-input:focus {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.25);
}
#chat-input::placeholder {
color: rgba(255, 255, 255, 0.3);
}
</style>
+4
View File
@@ -0,0 +1,4 @@
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
+296
View File
@@ -0,0 +1,296 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
body {
margin: 0;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
.counter {
font-size: 16px;
padding: 5px 10px;
border-radius: 5px;
color: var(--accent);
background: var(--accent-bg);
border: 2px solid transparent;
transition: border-color 0.3s;
margin-bottom: 24px;
&:hover {
border-color: var(--accent-border);
}
&:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
}
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
#app {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
#next-steps {
display: flex;
border-top: 1px solid var(--border);
text-align: left;
& > div {
flex: 1 1 0;
padding: 32px;
@media (max-width: 1024px) {
padding: 24px 20px;
}
}
.icon {
margin-bottom: 16px;
width: 22px;
height: 22px;
}
@media (max-width: 1024px) {
flex-direction: column;
text-align: center;
}
}
#docs {
border-right: 1px solid var(--border);
@media (max-width: 1024px) {
border-right: none;
border-bottom: 1px solid var(--border);
}
}
#next-steps ul {
list-style: none;
padding: 0;
display: flex;
gap: 8px;
margin: 32px 0 0;
.logo {
height: 18px;
}
a {
color: var(--text-h);
font-size: 16px;
border-radius: 6px;
background: var(--social-bg);
display: flex;
padding: 6px 12px;
align-items: center;
gap: 8px;
text-decoration: none;
transition: box-shadow 0.3s;
&:hover {
box-shadow: var(--shadow);
}
.button-icon {
height: 18px;
width: 18px;
}
}
@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
justify-content: center;
li {
flex: 1 1 calc(50% - 8px);
}
a {
width: 100%;
justify-content: center;
box-sizing: border-box;
}
}
}
#spacer {
height: 88px;
border-top: 1px solid var(--border);
@media (max-width: 1024px) {
height: 48px;
}
}
.ticks {
position: relative;
width: 100%;
&::before,
&::after {
content: '';
position: absolute;
top: -4.5px;
border: 5px solid transparent;
}
&::before {
left: 0;
border-left-color: var(--border);
}
&::after {
right: 0;
border-right-color: var(--border);
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"types": ["vite/client"],
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}
+7
View File
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: './', // Use relative paths for built assets (essential for package:// urls)
server: {
port: 3000 // Ensure dev server runs on 3000 to match settings.json expectation
},
build: {
outDir: '../client_packages/cef',
emptyOutDir: true,
}
})
+1013
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
{
"name": "develop_client",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite build --watch",
"build": "vite build"
},
"devDependencies": {
"typescript": "^5.0.0",
"vite": "^5.0.0"
},
"dependencies": {
"@entityseven/rage-fw-rpc": "^0.2.5"
}
}
+53
View File
@@ -0,0 +1,53 @@
import { Rpc } from '@entityseven/rage-fw-rpc';
const rpc = new Rpc();
let browser: BrowserMp | null = null;
// Initialize CEF via standard RAGE MP event to bootstrap the browser for RPC
mp.events.add('client:initCef', (cefUrl: string, isDebug: boolean) => {
if (isDebug) {
mp.gui.chat.push(`[Client] Initializing CEF: ${cefUrl}`);
}
if (browser) {
browser.destroy();
browser = null;
}
browser = mp.browsers.new(cefUrl);
rpc.browser = browser; // Link immediately
mp.gui.chat.push(`[Client] Browser created and linked to RPC`);
mp.gui.cursor.show(true, true);
});
// Fallback: auto-link any newly created browser if current is null
mp.events.add('browserCreated', (b: BrowserMp) => {
if (!rpc.browser) {
rpc.browser = b;
mp.gui.chat.push(`[Client] RPC Browser auto-linked via browserCreated`);
}
mp.gui.chat.show(false);
});
// Custom Chat Handlers via RPC
rpc.register('chat:toggleInput', (state: boolean) => {
// This allows you to freeze other inputs while typing if you build a UI manager later
});
rpc.register('chat:sendMessage', (msg: string) => {
// Send the message natively to the server via our RPC
rpc.callServer('server:chat:receive', [msg]);
});
// Listen for structured chat broadcasts from the server (Player chat)
rpc.register('chat:push:custom', (chatData: any) => {
rpc.callBrowser('chat:addMessage', [chatData]);
});
// Listen for incoming native string messages from the server (System prints/announcements)
mp.events.add('chat:push', (text: string) => {
rpc.callBrowser('chat:addMessage', [{ type: 'system', text: text }]);
});
export { rpc };
+13
View File
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"]
}
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
outDir: '../client_packages',
emptyOutDir: false,
lib: {
entry: resolve(__dirname, 'src/index.ts'),
formats: ['iife'],
name: 'clientBundle',
fileName: () => 'client.js'
}
}
});
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@apex-freeroam/core",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@apex-freeroam/core",
"version": "1.0.0",
"dependencies": {
"@entityseven/rage-fw-rpc": "^0.2.5"
}
},
"node_modules/@entityseven/rage-fw-rpc": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@entityseven/rage-fw-rpc/-/rage-fw-rpc-0.2.5.tgz",
"integrity": "sha512-U9V+eZTQbkL+JZEU3TS+fCroPyvSvBXICLikcC/h9gCNHhFiS6vm+W+92LqGzBi3K+aZEnsx57hAhhObB03g0w==",
"license": "MIT",
"peerDependencies": {
"typescript": "^5"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "@apex-freeroam/core",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@entityseven/rage-fw-rpc": "^0.2.5"
}
}
+65
View File
@@ -0,0 +1,65 @@
const fs = require('fs');
const path = require('path');
const { Rpc } = require('@entityseven/rage-fw-rpc');
const rpc = new Rpc();
let settings;
try {
const settingsPath = path.resolve(__dirname, '../../settings.json');
const data = fs.readFileSync(settingsPath, 'utf8');
settings = JSON.parse(data);
console.log('[Core] Loaded settings.json successfully.');
} catch (e) {
console.error('[Core] Failed to load settings.json:', e);
settings = { cef: 'dev', debugger: true };
}
mp.events.add('playerReady', (player) => {
console.log(`[Core] Player ${player.name} joined. Initializing CEF via Rpc...`);
// Determine the CEF URL based on settings
const isDev = settings.cef === 'dev';
const cefUrl = isDev ? 'http://localhost:3000' : 'package://cef/index.html';
// Give the client a tiny bit of time to fully load scripts before calling
setTimeout(() => {
if (!mp.players.exists(player)) return;
player.call('client:initCef', [cefUrl, !!settings.debugger]);
console.log(`[Core] CEF initialization event sent to ${player.name}`);
}, 500);
});
// Custom Chat Backend
rpc.register('server:chat:receive', (player, msg) => {
if (msg.startsWith('/')) {
console.log(`[Command] ${player.name}: ${msg}`);
// Command execution logic can be wired here later
rpc.callClient(player, 'chat:push:custom', [{
type: 'error',
text: 'Commands are not fully implemented in the custom chat yet.'
}]).catch(() => {});
return true;
}
// Broadcast structured message to all players
const chatData = {
type: 'player',
name: player.name,
id: player.id,
text: msg,
timestamp: Date.now()
};
mp.players.forEach((p) => {
rpc.callClient(p, 'chat:push:custom', [chatData]).catch(() => {});
});
return true;
});
// Example RPC handler
rpc.register('server:test', (player, msg) => {
console.log(`[RPC] Received from ${player.name}: ${msg}`);
return `Server received: ${msg}`;
});
+10
View File
@@ -0,0 +1,10 @@
{
"mysql": {
"host": "localhost",
"user": "root",
"password": "",
"database": "apex_freeroam"
},
"debugger": true,
"cef": "dev"
}