├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── SECURITY.asc ├── app.config.ts ├── app.vue ├── assets ├── main.css └── reden-alipay.png ├── components ├── CommonCaptcha.vue ├── Empire.vue ├── RedStoneSection.vue ├── RedenRouter.vue ├── RedstoneSectionTitle.vue ├── Sumimasen.vue ├── UserBadges.vue ├── admin │ ├── AdminBanUserButton.vue │ └── AdminEditUserButton.vue ├── ads │ ├── BlockAd.vue │ ├── BottomBarAd.vue │ ├── GoogleAd.vue │ └── SidebarAd.vue ├── content │ ├── ProseA.vue │ ├── ProseBlockquote.vue │ ├── ProseCode.vue │ ├── ProseEm.vue │ ├── ProseH1.vue │ ├── ProseH2.vue │ ├── ProseH3.vue │ ├── ProseH4.vue │ ├── ProseH5.vue │ ├── ProseH6.vue │ ├── ProseHr.vue │ ├── ProseImg.vue │ ├── ProseLi.vue │ ├── ProseOl.vue │ ├── ProseP.vue │ ├── ProsePre.vue │ ├── ProseScript.vue │ ├── ProseStrong.vue │ ├── ProseTable.vue │ ├── ProseTbody.vue │ ├── ProseTd.vue │ ├── ProseTh.vue │ ├── ProseThead.vue │ ├── ProseTr.vue │ ├── ProseUl.vue │ └── mdc-styles.css ├── homePage │ └── RedstonePostCard.vue ├── layout │ ├── Header.vue │ └── footer.vue ├── litematica │ ├── LitematicaGenDownloader.vue │ ├── LitematicaShareDownloader.vue │ ├── LitematicaUpload.vue │ ├── MinecraftFarmCard.vue │ ├── RedenPostStatusChip.vue │ ├── SizeInput.vue │ └── TransferOwnershipDialog.vue ├── minecraft │ ├── ItemDisplay.vue │ └── LitematicaPreview.vue ├── misc │ └── OAuthButtons.vue └── profile │ ├── UserContentPanel.vue │ ├── UserProfileCard.vue │ ├── VerifyMinecraft.vue │ └── user-card-wrap.css ├── content ├── docs │ └── test.md ├── tos.md └── zh_cn │ └── docs │ ├── generator-rules.md │ └── real-person.md ├── crowdin.yml ├── deploy.sh ├── error.vue ├── i18n.config.ts ├── i18n ├── en.json ├── en.ts ├── minecraft │ ├── en_us.json │ ├── ru_ru.json │ ├── zh_cn.json │ └── zh_tw.json ├── ru.ts ├── ru_RU.json ├── zh_CN.json ├── zh_TW.json ├── zh_cn.ts └── zh_tw.ts ├── indexnow.py ├── layouts └── default.vue ├── middleware ├── 00.login-guard.global.ts └── 01.title.global.ts ├── nuxt.config.ts ├── package.json ├── pages ├── @[username].vue ├── admin │ ├── index.vue │ ├── security-logs.vue │ └── users.vue ├── community-guidelines.vue ├── dmca.vue ├── docs │ └── [...slug].vue ├── download.vue ├── empire │ ├── index.vue │ └── tutorial.vue ├── feature │ ├── debugger.vue │ ├── index.vue │ ├── rvc.vue │ └── undo.vue ├── forgot-password.vue ├── home │ ├── edit.vue │ └── index.vue ├── index.vue ├── litematica │ ├── [name].vue │ ├── edit.vue │ ├── index.vue │ └── review.vue ├── login.vue ├── miku.vue ├── preview.vue ├── register.vue ├── reset-password.vue ├── sponsors.vue ├── studio.vue └── x-backup │ ├── me.vue │ └── plans.vue ├── plugins ├── icons │ ├── Bilibili.vue │ ├── CubeScan.vue │ ├── DiscordIcon.vue │ ├── Microsoft.vue │ ├── ModrinthFull.vue │ ├── QuarkCloud.vue │ ├── YouTube_Logo_2017.vue │ ├── ZipArchive.vue │ ├── customSvgs.ts │ └── google.vue └── vuetify.ts ├── public ├── ads.txt ├── cae43db142664664989d50b7c690d397.txt ├── favicon.ico ├── image │ ├── generals │ │ ├── city.png │ │ ├── crown.png │ │ ├── mountain.png │ │ ├── obstacle.png │ │ └── swamp.png │ ├── homepage │ │ ├── section │ │ │ ├── lever.png │ │ │ ├── lever_base.png │ │ │ ├── piston_base.png │ │ │ ├── piston_head.png │ │ │ ├── piston_side.png │ │ │ ├── redstone_dust_0.png │ │ │ ├── redstone_dust_15.png │ │ │ ├── redstone_lamp.png │ │ │ └── redstone_lamp_on.png │ │ └── undo │ │ │ ├── TNT.webp │ │ │ ├── chest.png │ │ │ ├── creeper.webp │ │ │ ├── grass_block.webp │ │ │ └── villager.webp │ ├── reden-alipay.png │ └── sumimasen.png ├── litematica │ └── atlas.png ├── reden_256.png ├── robots.txt └── video │ └── nether_portal.mp4 ├── script ├── fix-i18n.js └── init-git-hash.js ├── server └── tsconfig.json ├── store ├── app.ts ├── message.ts └── meta.ts ├── tsconfig.json ├── utils ├── api.ts ├── conditionParser.ts ├── constants.ts ├── litematic-utils.ts └── litematica │ ├── assets.js │ ├── atlas.png │ ├── models_selectable.json │ └── opaque.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | 'prettier', 11 | ], 12 | rules: { 13 | 'vue/multi-word-component-names': 'off', 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | .vscode 8 | dist 9 | assets/hash.json 10 | 11 | # Node dependencies 12 | node_modules 13 | 14 | # Logs 15 | logs 16 | *.log 17 | 18 | # Misc 19 | .DS_Store 20 | .fleet 21 | .idea 22 | 23 | # Local env files 24 | .env 25 | .env.* 26 | !.env.example 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | utils/litematica/* 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/README.md -------------------------------------------------------------------------------- /SECURITY.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 50 | +bM1i7uF 51 | =HdeZ 52 | -----END PGP PUBLIC KEY BLOCK----- 53 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | title: 'Hello Nuxt', 3 | theme: { 4 | dark: true, 5 | colors: { 6 | primary: '#ff0000', 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 59 | 60 | 61 | 73 | -------------------------------------------------------------------------------- /assets/main.css: -------------------------------------------------------------------------------- 1 | .content-common { 2 | min-width: 320px; 3 | max-width: 800px; 4 | padding: 20px; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | 9 | a.router { 10 | text-decoration: none; 11 | display: inline-block; 12 | 13 | /*noinspection CssUnresolvedCustomProperty*/ 14 | color: rgb(var(--v-theme-primary-darken-1)); 15 | transition: all 0.5s ease-in-out; 16 | } 17 | 18 | .v-footer a { 19 | text-decoration: none; 20 | 21 | color: inherit; 22 | opacity: 0.8; 23 | transition: all 0.5s ease-in-out; 24 | } 25 | 26 | .v-main a.router { 27 | /*noinspection CssUnresolvedCustomProperty*/ 28 | color: rgb(var(--v-theme-primary-darken-1)); 29 | } 30 | 31 | a.router:hover { 32 | /*noinspection CssUnresolvedCustomProperty*/ 33 | color: rgb(var(--v-theme-primary)); 34 | opacity: 1; 35 | text-decoration: underline; 36 | 37 | transition: all 0.5s ease-in-out; 38 | } 39 | 40 | .v-footer a:hover { 41 | color: inherit; 42 | opacity: 1; 43 | text-decoration: underline; 44 | 45 | transition: all 0.5s ease-in-out; 46 | } 47 | 48 | .z-10 { 49 | z-index: 10; 50 | } 51 | 52 | .z-20 { 53 | z-index: 20; 54 | } 55 | -------------------------------------------------------------------------------- /assets/reden-alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/assets/reden-alipay.png -------------------------------------------------------------------------------- /components/CommonCaptcha.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /components/RedenRouter.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 79 | -------------------------------------------------------------------------------- /components/RedstoneSectionTitle.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 69 | 70 | 129 | -------------------------------------------------------------------------------- /components/Sumimasen.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /components/UserBadges.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /components/admin/AdminBanUserButton.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 128 | -------------------------------------------------------------------------------- /components/admin/AdminEditUserButton.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 137 | -------------------------------------------------------------------------------- /components/ads/BlockAd.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | 28 | -------------------------------------------------------------------------------- /components/ads/BottomBarAd.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 36 | -------------------------------------------------------------------------------- /components/ads/GoogleAd.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 79 | 80 | 85 | -------------------------------------------------------------------------------- /components/ads/SidebarAd.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /components/content/ProseA.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | -------------------------------------------------------------------------------- /components/content/ProseBlockquote.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseCode.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /components/content/ProseEm.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseH1.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /components/content/ProseH2.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /components/content/ProseH3.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /components/content/ProseH4.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /components/content/ProseH5.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /components/content/ProseH6.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /components/content/ProseHr.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /components/content/ProseImg.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 45 | -------------------------------------------------------------------------------- /components/content/ProseLi.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /components/content/ProseOl.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseP.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /components/content/ProsePre.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 33 | 34 | 39 | -------------------------------------------------------------------------------- /components/content/ProseScript.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /components/content/ProseStrong.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseTable.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseTbody.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseTd.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseTh.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseThead.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseTr.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/ProseUl.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /components/content/mdc-styles.css: -------------------------------------------------------------------------------- 1 | .my-mdc-header { 2 | display: block; 3 | font-weight: 800; 4 | color: inherit; 5 | text-decoration: none; 6 | margin-block-start: 0.6em; 7 | margin-block-end: 0.3em; 8 | } 9 | 10 | .my-mdc-header:hover { 11 | text-decoration: underline; 12 | } 13 | 14 | li.my-mdc-li { 15 | margin-left: 24px !important; 16 | } 17 | 18 | [id] { 19 | scroll-margin-top: 100px; 20 | } 21 | -------------------------------------------------------------------------------- /components/homePage/RedstonePostCard.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 53 | -------------------------------------------------------------------------------- /components/litematica/LitematicaGenDownloader.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 121 | -------------------------------------------------------------------------------- /components/litematica/MinecraftFarmCard.vue: -------------------------------------------------------------------------------- 1 | 28 | 123 | 213 | -------------------------------------------------------------------------------- /components/litematica/RedenPostStatusChip.vue: -------------------------------------------------------------------------------- 1 | 14 | 28 | -------------------------------------------------------------------------------- /components/litematica/SizeInput.vue: -------------------------------------------------------------------------------- 1 | 38 | 106 | 124 | -------------------------------------------------------------------------------- /components/litematica/TransferOwnershipDialog.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 89 | -------------------------------------------------------------------------------- /components/minecraft/ItemDisplay.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /components/misc/OAuthButtons.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 48 | 49 | -------------------------------------------------------------------------------- /components/profile/UserContentPanel.vue: -------------------------------------------------------------------------------- 1 | 51 | 104 | -------------------------------------------------------------------------------- /components/profile/VerifyMinecraft.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /components/profile/user-card-wrap.css: -------------------------------------------------------------------------------- 1 | .user-card-wrap { 2 | min-width: 330px; 3 | height: min-content; 4 | } 5 | 6 | @media (min-width: 960px) { 7 | .user-card-wrap { 8 | position: sticky; 9 | top: 64px; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /content/docs/test.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Title of the page' 3 | description: 'meta description of the page' 4 | --- 5 | 6 | # Hello World! 7 | 8 | This is a simple markdown file with some content. 9 | 10 | ```python 11 | def hello_world(): 12 | print("Hello World!") 13 | ``` 14 | 15 | ASdcav 16 | 17 | ::alert{type="info"} 18 | Check out an **info** alert with `code` and a [link](/). 19 | :: 20 | 21 | ::alert{type="success"} 22 | Check out a **success** alert with `code` and a [link](/). 23 | :: 24 | 25 | ::alert{type="warning"} 26 | Check out a **warning** alert with `code` and a [link](/). 27 | :: 28 | 29 | ::alert{type="danger"} 30 | Check out a **danger** alert with `code` and a [link](/). 31 | :: 32 | 33 | [This is a link](/) 34 | 35 | [Link to Litematica generator](/litematica) 36 | 37 | ## Alpha 38 | 39 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec urna tellus, condimentum quis porta nec, malesuada ac metus. Vivamus non ex orci. Maecenas finibus diam vitae imperdiet varius. Suspendisse turpis ipsum, vehicula a feugiat in, congue at quam. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut semper pellentesque dolor, et dictum lacus. In a sollicitudin augue. Aenean vitae leo nec elit aliquet tempor vitae dictum ligula. Pellentesque bibendum lectus orci, sed placerat elit iaculis vitae. 40 | 41 | ## Bravo 42 | 43 | Aliquam ac dolor venenatis, fringilla libero iaculis, sodales ligula. Donec convallis laoreet enim, ac congue erat dignissim in. Suspendisse potenti. Nullam varius metus id turpis tincidunt, non placerat mauris rutrum. Aliquam non sem vitae mi posuere eleifend. Integer arcu mauris, pretium eu felis sit amet, dapibus mattis ipsum. Nulla suscipit laoreet dignissim. Proin in leo et massa scelerisque rutrum eu et nibh. Suspendisse eleifend vulputate enim, a mollis risus. 44 | 45 | ## Charlie 46 | 47 | Suspendisse rhoncus orci et libero venenatis aliquet. Nulla sit amet eros aliquam, dapibus est a, laoreet nulla. Duis tincidunt vestibulum aliquam. Suspendisse potenti. Donec venenatis risus nibh, ut bibendum dolor gravida non. Pellentesque eget nunc nibh. Vestibulum ultrices hendrerit massa at varius. Fusce vestibulum diam libero, quis luctus mauris gravida in. Curabitur in dui interdum justo volutpat volutpat. Nulla risus ipsum, faucibus a odio vel, ultrices mollis lorem. Integer placerat tincidunt ligula sit amet tincidunt. Nullam ullamcorper viverra hendrerit. Donec eget auctor lectus. Proin nisl lorem, maximus et bibendum non, convallis ac elit. Nullam sagittis metus sem, eget mattis odio condimentum id. Pellentesque vulputate sem quis tellus aliquam bibendum. 48 | 49 | ## Delta 50 | 51 | Cras pharetra posuere ipsum sit amet consequat. Quisque venenatis erat ac ligula tristique porttitor. Phasellus porta condimentum felis, consectetur molestie mauris congue et. Maecenas dapibus risus et ultricies efficitur. Curabitur nunc purus, sagittis at justo varius, finibus semper ligula. Ut vitae sagittis odio. Aenean bibendum massa sed diam pellentesque bibendum. Suspendisse nibh velit, dignissim in posuere id, posuere id eros. Maecenas elementum, est eu blandit congue, tortor purus rhoncus nunc, luctus consectetur quam metus quis dolor. Donec venenatis aliquam lacinia. Maecenas dictum, erat interdum consequat convallis, tortor lacus tincidunt ex, sed congue odio mi elementum dolor. Fusce ac nisl ornare, lobortis neque eu, dapibus arcu. Nunc non tempor lorem. Nulla ultricies ligula nibh, pharetra tempus dolor tincidunt et. Nunc semper, quam ac congue sagittis, nisl orci convallis ante, quis volutpat odio odio quis nisi. Curabitur iaculis suscipit eros, ut gravida felis tempor quis. 52 | 53 | ## Echo 54 | 55 | Curabitur consectetur, lorem ut laoreet fringilla, eros libero convallis elit, in efficitur leo sapien commodo nulla. Phasellus finibus orci justo, tempor tincidunt enim consequat facilisis. Vivamus ipsum nibh, blandit a massa non, cursus tincidunt neque. Nunc eget commodo risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin mattis sapien quis mollis condimentum. Nunc cursus, tortor id ultrices tempor, tellus massa pharetra mauris, in malesuada mauris ante eget justo. Maecenas ornare, lorem id rutrum congue, risus nisi viverra urna, eget fermentum felis libero eu mauris. Nullam id justo fermentum felis lacinia hendrerit. 56 | 57 | ## Foxtrot 58 | 59 | Nulla ut erat eget tellus bibendum tincidunt id vitae purus. Fusce augue enim, sagittis sit amet leo eget, commodo aliquam augue. Morbi volutpat ipsum sed mi porttitor porttitor. Sed non mattis enim. Fusce sollicitudin ultrices orci. Vivamus finibus malesuada sapien vel condimentum. Aliquam congue rhoncus euismod. Morbi lacinia purus vitae quam rhoncus, a tempor magna convallis. Morbi nec augue nibh. Praesent sit amet ipsum ac tellus malesuada pretium sit amet id sem. Nam fringilla nisl at condimentum molestie. Maecenas ullamcorper nisl nec leo rutrum, sed tristique est facilisis. 60 | 61 | ## Golf 62 | 63 | Pellentesque sed sollicitudin turpis. Morbi ornare odio vel risus sollicitudin placerat. Pellentesque dignissim nibh nunc, ut aliquet sapien iaculis nec. Duis nec purus sed lectus dapibus porttitor a nec ligula. Aenean id pellentesque dui, ac rhoncus lectus. Maecenas vitae faucibus dui, quis finibus enim. Proin tristique dolor neque, id mollis dolor tincidunt ac. Nunc aliquet eget lorem vel consectetur. Fusce in sem ex. Maecenas ac augue posuere, tincidunt ante eu, consectetur est. Nullam ultrices enim quis nisl vestibulum molestie. 64 | 65 | ## Hotel 66 | 67 | In tortor dui, dictum quis diam eu, semper varius mauris. Nullam ultrices eleifend sem, vitae interdum dolor tristique vitae. Nam lacinia at dolor ac scelerisque. Nunc eleifend faucibus sagittis. Morbi iaculis purus euismod leo molestie congue. Sed egestas non mi id ullamcorper. Vestibulum non enim at odio feugiat porta. 68 | 69 | ## India 70 | 71 | Vestibulum condimentum, nibh a pretium vehicula, massa erat efficitur sapien, non porttitor orci lectus a orci. Integer vehicula urna nec lobortis elementum. Cras vitae enim quis magna blandit ultrices. Aenean blandit posuere ante, vel sodales turpis lobortis ut. In dui nisl, mattis sed rhoncus egestas, viverra quis lorem. Vivamus gravida vel lectus id vulputate. Etiam ac mauris feugiat, eleifend ipsum in, ultrices neque. Duis a feugiat mi, ut maximus nulla. Suspendisse dignissim lorem arcu, ac vehicula magna blandit et. Cras felis arcu, finibus et aliquet vitae, pretium non diam. Ut viverra sollicitudin arcu, et venenatis risus cursus iaculis. Ut id aliquam dolor. Mauris tincidunt ante justo, nec porttitor ligula bibendum in. Ut et justo quis risus maximus finibus. 72 | 73 | ## Juliet 74 | 75 | Phasellus blandit nec nibh quis laoreet. Donec ultrices orci nisl, sagittis pulvinar nunc semper non. Nullam elementum erat felis, ut rutrum lectus tristique sed. Sed tempus turpis vitae lacus eleifend vulputate vel a dolor. Nunc sollicitudin tellus mattis vulputate porttitor. Maecenas ullamcorper quis quam quis aliquet. Sed id massa sagittis, pretium lorem at, dignissim tellus. Donec feugiat metus id efficitur bibendum. 76 | -------------------------------------------------------------------------------- /content/tos.md: -------------------------------------------------------------------------------- 1 | **用户协议** 2 | 3 | **欢迎使用 Reden!** 4 | 5 | 本协议是您(以下简称“用户”或“您”)与Reden(以下简称“我们”)之间就您使用 redenmc.com(以下简称“本网站”)及其提供的服务(以下简称“本服务”)所订立的具有法律约束力的协议。请您仔细阅读本协议,一旦您访问、使用本网站或接受本服务,即表示您已阅读、理解并同意接受本协议的所有条款。如果您不同意本协议的任何条款,请立即停止使用本网站及本服务。 6 | 7 | **1. 服务内容** 8 | 9 | 本网站提供用户注册、上传设计、分享设计、浏览设计、下载设计、在线预览等诸多功能。 10 | 11 | **2. 用户账户** 12 | 13 | 2.1 **注册:** 您需要注册一个账户才能使用本服务的某些功能。注册时,您需要提供真实、准确、完整的信息。您有责任维护您的账户和密码的机密性,并对使用您的账户进行的所有活动负责。 14 | 15 | 2.2 **账户安全:** 您同意不向任何第三方透露您的账户信息。如发现任何未经授权使用您账户的情况,请立即通知我们。 16 | 17 | 2.3 **账户终止:** 我们保留在以下情况下终止您的账户的权利,包括但不限于: 18 | 19 | - 您违反本协议的任何条款; 20 | - 您侵犯他人权益; 21 | - 根据法律法规或政府部门的要求。 22 | 23 | **3. 用户内容** 24 | 25 | 3.1 **内容所有权:** 您上传至本网站的内容(以下简称“用户内容”)的所有权归您所有。 26 | 27 | 3.2 **内容许可:** 对于您上传的内容,您授予本网站一项全球性的、非独家的、可转让的许可,允许本网站为了提供、运营、推广和改进本服务而使用、复制、修改、改编、发布、翻译、分发、公开展示和公开表演您的用户内容。您也可以自行指定任何其他授权协议,只要对本网站授予所有上述权利。 28 | 29 | 3.3 **禁止内容:** 您不得上传以下内容: 30 | 31 | - 违反中国法律法规的内容,包括但不限于: 32 | - 危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的内容; 33 | - 损害国家荣誉和利益的内容; 34 | - 煽动民族仇恨、民族歧视,破坏民族团结的内容; 35 | - 破坏国家宗教政策,宣扬邪教和封建迷信的内容; 36 | - 散布谣言,扰乱社会秩序,破坏社会稳定的内容; 37 | - 散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的内容; 38 | - 侮辱或者诽谤他人,侵害他人合法权益的内容; 39 | - 侵犯他人知识产权的内容; 40 | - 包含病毒、恶意软件或其他有害代码的内容; 41 | - 其他我们认为不适当的内容。 42 | 43 | 3.4 **内容审核:** 我们有权(但无义务)审核用户内容,并有权删除任何违反本协议的内容。 44 | 45 | **4. 知识产权** 46 | 47 | 4.1 **本网站的知识产权:** 本网站及其所有内容(除用户内容外),包括但不限于文本、图形、徽标、图像、软件等,均受著作权、商标权和其他知识产权法律的保护,归本网站所有。 48 | 49 | 4.2 **侵权处理:** 如果您认为本网站上的任何内容侵犯了您的知识产权,请通过 me@redenmc.com 与我们联系。 50 | 51 | **5. 免责声明** 52 | 53 | 在法律允许的最大范围内,本网站不对因使用本服务而产生的任何直接、间接、附带、特殊、惩罚性或后果性损害承担责任。本网站不对用户内容的准确性、完整性或合法性负责。 54 | 55 | **6. 服务变更和终止** 56 | 57 | 我们保留随时修改、暂停或终止本服务的权利,并会尽力提前通知用户。 58 | 59 | **7. 隐私政策** 60 | 61 | 我们重视用户隐私。我们的隐私政策[链接到您的隐私政策]解释了我们如何收集、使用和保护您的个人信息。 62 | 63 | **8. 适用法律和争议解决** 64 | 65 | 本协议受中华人民共和国法律管辖。因本协议引起的任何争议,应通过友好协商解决。协商不成的,应提交至 陕西省西安市碑林区人民法院 诉讼解决。 66 | 67 | **9. 其他** 68 | 69 | 9.1 **未成年人保护:**如果您的服务面向未成年人,请添加相应的保护条款。 70 | 71 | 9.2 **联系方式:** 如果您有任何问题或疑问,请通过 me@redenmc.com 与我们联系。 72 | 73 | **请再次强调:** 本协议仅为草稿,请务必咨询专业的法律人士进行审核和修改,以确保其符合中国法律法规的要求。尤其需要关注与内容审核、信息安全、知识产权等相关的法律法规。 74 | -------------------------------------------------------------------------------- /content/zh_cn/docs/generator-rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: '投影生成器规则' 3 | tags: 4 | - 投影 5 | - 投影生成器 6 | contributors: 7 | - zly2006 8 | --- 9 | 10 | 以下是投影生成器的生成规则,这主要是为了世吞等机器的自动堆叠而制订的。当然,这也适用于很多其他的机器。 11 | 12 | ## 基本生成规则 13 | 14 | 按各个(xyz)轴的子区域数量,其中,如果该轴有一个子区域,那么会生成在相同的位置;如果该轴有两个子区域,则按照这两个区域到边上的距离生成,想象一下世吞的出发站和返回站;如果有三个以上,那么左右两边和两个子区规则一样,中间的部分会进行重复,中间的部分可以有多个宽度不等的子区域,对于任意用户输入的最终宽度,程序会自动计算组合策略。 15 | 16 | ## 分层生成 17 | 18 | 每个层内同基本生成规则,子区域名需要这么写:层名+英语斜线+区域名,如 返回站/头部。 19 | 可以用双斜线圈一部分子区域,用于测试(比如多放一个重复体,或什么用都没有的连杆),用两个斜线(//)开头,会被生成器忽略。 20 | -------------------------------------------------------------------------------- /content/zh_cn/docs/real-person.md: -------------------------------------------------------------------------------- 1 | # 实名认证详细介绍 2 | 3 | 根据《互联网论坛社区服务管理规定》,为了维护论坛社区的健康发展,保障用户的合法权益,我们要求所有中国大陆用户进行实名认证。 4 | 5 | 未经实名认证的用户,或者不是中华人民共和国公民的用户,其发布的内容对于中国大陆地区用户将无法查看,对于其他地区用户可以正常显示。 6 | 7 | ## 实名认证的方式 8 | 9 | 1. 下单并支付实名认证费用(人民币 0.5 元,[支付宝API必须的费用](https://opendocs.alipay.com/open/02zlo2?pathHash=efd3543f#%E8%AE%A1%E8%B4%B9%E6%A8%A1%E5%BC%8F),并非以此实现盈利)。 10 | 2. 输入正确的姓名和身份证号码、本人手机号码。 11 | 3. 输入短信验证码,完成实名认证。 12 | 4. 如果实名验证不通过(不是本人/不是本人手机号,请仔细确认),需重新缴纳实名认证费用;如果短信未收到,3分钟内可重新发送,超过3分钟需重新缴纳实名认证费用。 13 | 14 | ## 实名认证隐私政策 15 | 16 | 1. 实名认证信息仅用于论坛社区服务管理,例如永久封禁某个账户,不会用于其他用途。 17 | 2. 除非法律法规要求,实名认证信息不会被泄露给第三方。 18 | 3. 实名认证信息不会被用于广告推送。 19 | 4. 实名认证信息会被加密存储,保障用户的隐私安全。 20 | 5. 账号注销后,实名认证信息会被删除。如果之后重新注册账号,需要重新进行实名认证并支付费用。 21 | 22 | ## 中国境外用户 23 | 24 | 中国境外用户(根据IP判断)不受实名认证政策的限制,可以正常浏览和发布内容。但是他们发布的内容对于中国大陆地区用户将无法查看。 25 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /i18n/en.json 3 | translation: /i18n/%locale_with_underscore%.json 4 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | echo "\"$(git rev-parse --short HEAD)\"" > assets/hash.json 2 | 3 | nuxi build && \ 4 | rsync -va .output/ zly@dcdccssy.cn:/www/website/node/ 5 | 6 | ssh zly@dcdccssy.cn 'kill -9 $(lsof -t -i:3000); cd /www/website/node/ && screen -d -m bash -c "node server/index.mjs"' 7 | echo 1 | ssh zly@dcdccssy.cn '~/mt.py' 8 | 9 | #nuxi generate && \ 10 | #rsync -va .output/public/ zly@dcdccssy.cn:/www/website/prod/ 11 | -------------------------------------------------------------------------------- /error.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /i18n.config.ts: -------------------------------------------------------------------------------- 1 | import en from '@/i18n/en.json'; 2 | import zh_cn from '@/i18n/zh_CN.json'; 3 | import zh_tw from '@/i18n/zh_TW.json'; 4 | import ru from '@/i18n/ru_RU.json'; 5 | import mcEn from '@/i18n/minecraft/en_us.json'; 6 | import mcZhCn from '@/i18n/minecraft/zh_cn.json'; 7 | import mcZhTw from '@/i18n/minecraft/zh_tw.json'; 8 | import mcRu from '@/i18n/minecraft/ru_ru.json'; 9 | 10 | export const localeToIso: Record = { 11 | en: 'en', 12 | zh_cn: 'zh-CN', 13 | zh_tw: 'zh-TW', 14 | ru: 'ru', 15 | }; 16 | 17 | const messages = { 18 | en: { 19 | ...en, 20 | minecraft: mcEn, 21 | }, 22 | zh_cn: { 23 | ...zh_cn, 24 | minecraft: mcZhCn, 25 | }, 26 | zh_tw: { 27 | ...zh_tw, 28 | minecraft: mcZhTw, 29 | }, 30 | ru: { 31 | ...ru, 32 | minecraft: mcRu, 33 | }, 34 | }; 35 | export default defineI18nConfig(() => ({ 36 | legacy: false, // Vuetify does not support the legacy mode of vue-i18n 37 | fallbackLocale: 'en', 38 | messages, 39 | })); 40 | -------------------------------------------------------------------------------- /i18n/en.ts: -------------------------------------------------------------------------------- 1 | import en from '~/i18n/en.json'; 2 | import mcEn from '~/i18n/minecraft/en_us.json'; 3 | 4 | export default { 5 | ...en, 6 | minecraft: mcEn, 7 | }; 8 | -------------------------------------------------------------------------------- /i18n/ru.ts: -------------------------------------------------------------------------------- 1 | import ru from '~/i18n/ru_RU.json'; 2 | import mcRu from '~/i18n/minecraft/ru_ru.json'; 3 | 4 | export default { 5 | ...ru, 6 | minecraft: mcRu, 7 | }; 8 | -------------------------------------------------------------------------------- /i18n/zh_cn.ts: -------------------------------------------------------------------------------- 1 | import zh_cn from '~/i18n/zh_CN.json'; 2 | import mcZhCn from '~/i18n/minecraft/zh_cn.json'; 3 | 4 | export default { 5 | ...zh_cn, 6 | minecraft: mcZhCn, 7 | }; 8 | -------------------------------------------------------------------------------- /i18n/zh_tw.ts: -------------------------------------------------------------------------------- 1 | import zh_tw from '~/i18n/zh_TW.json'; 2 | import mcZhTw from '~/i18n/minecraft/zh_tw.json'; 3 | 4 | export default { 5 | ...zh_tw, 6 | minecraft: mcZhTw, 7 | }; 8 | -------------------------------------------------------------------------------- /indexnow.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | 5 | host = "redenmc.com" 6 | key = "cae43db142664664989d50b7c690d397" 7 | 8 | json = { 9 | "host": host, 10 | "key": key, 11 | "keyLocation": f"https://redenmc.com/{key}.txt", 12 | "urlList": [] 13 | } 14 | 15 | os.path.exists('.output/public/__sitemap__/index.xml') 16 | # list files 17 | for file in os.listdir('.output/public/__sitemap__/'): 18 | if file.endswith('.xml'): 19 | with open(f'.output/public/__sitemap__/{file}', 'r') as f: 20 | content = f.read() 21 | # find tags 22 | urls = content.split('') 23 | urls.pop(0) 24 | for url in urls: 25 | url = url.split('')[0] 26 | json['urlList'].append(url) 27 | 28 | print(json) 29 | print('Found', len(json['urlList']), 'urls') 30 | input('Press enter to continue') 31 | 32 | requests.post('https://api.indexnow.org/IndexNow', json=json) 33 | -------------------------------------------------------------------------------- /middleware/00.login-guard.global.ts: -------------------------------------------------------------------------------- 1 | import { useAppStore } from '~/store/app'; 2 | 3 | export default defineNuxtRouteMiddleware((to, from) => { 4 | if (import.meta.server) return; 5 | const localePath = useLocalePath(); 6 | const appStore = useAppStore(); 7 | const meta: Record = { ...to.meta }; 8 | if (import.meta.dev) { 9 | console.log('[login-route-guard] page meta=', meta); 10 | } 11 | if (meta.needLogin === true && !appStore.logined) { 12 | if (import.meta.dev) { 13 | console.log('[login-route-guard] need login!'); 14 | } 15 | return navigateTo(localePath('/login')); 16 | } 17 | if (meta.needAdmin === true && appStore.userCache?.isStaff !== true) { 18 | if (import.meta.dev) { 19 | console.log('[login-route-guard] need admin!'); 20 | } 21 | return navigateTo(localePath('/login')); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /middleware/01.title.global.ts: -------------------------------------------------------------------------------- 1 | import { useNuxtApp } from '#app'; 2 | 3 | export default defineNuxtRouteMiddleware((to, from) => { 4 | const { t } = useNuxtApp().$i18n; 5 | if (to.meta.title && typeof to.meta.title === 'string') { 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | import vuetify from 'vite-plugin-vuetify'; 3 | import { createResolver } from '@nuxt/kit'; 4 | import transformAssetUrls = vuetify.transformAssetUrls; 5 | 6 | const { resolve } = createResolver(import.meta.url); 7 | 8 | let isPrerender = false; 9 | if ( 10 | process.argv.find((arg) => arg.includes('/nuxi')) && 11 | (process.argv.includes('build') || process.argv.includes('generate')) // nuxi build command 12 | ) { 13 | isPrerender = true; 14 | console.log('\x1b[32m😭 Pre-rendering'); 15 | } 16 | 17 | export default defineNuxtConfig({ 18 | compatibilityDate: '2024-04-03', 19 | devtools: { enabled: true }, 20 | build: { 21 | transpile: ['vuetify'], 22 | }, 23 | modules: [ 24 | '@vueuse/nuxt', 25 | '@pinia/nuxt', 26 | '@nuxtjs/i18n', 27 | '@nuxtjs/sitemap', 28 | '@nuxt/content', 29 | '@nuxtjs/mdc', 30 | '@nuxtjs/color-mode', 31 | '@ant-design-vue/nuxt', 32 | '@vite-pwa/nuxt', 33 | // cause OOM 34 | // '@nuxthq/studio', 35 | (_options, nuxt) => { 36 | nuxt.hooks.hook('vite:extendConfig', (config) => { 37 | // @ts-expect-error 38 | config.plugins.push(vuetify({ autoImport: true })); 39 | }); 40 | }, 41 | ], 42 | content: { 43 | // cause OOM 44 | // documentDriven: true, 45 | highlight: { 46 | theme: { 47 | dark: 'github-dark', 48 | default: 'github-light', 49 | }, 50 | preload: [ 51 | 'json', 52 | 'js', 53 | 'ts', 54 | 'html', 55 | 'css', 56 | 'vue', 57 | 'diff', 58 | 'shell', 59 | 'markdown', 60 | 'yaml', 61 | 'bash', 62 | 'ini', 63 | ], 64 | }, 65 | navigation: { 66 | fields: ['icon', 'titleTemplate', 'header', 'main', 'aside', 'footer'], 67 | }, 68 | api: { 69 | baseURL: '/_my_content', 70 | }, 71 | }, 72 | colorMode: { 73 | classSuffix: '', 74 | dataValue: 'theme', 75 | }, 76 | i18n: { 77 | strategy: 'prefix_and_default', 78 | vueI18n: './i18n.config.ts', 79 | defaultLocale: 'en', 80 | lazy: true, 81 | locales: [ 82 | { code: 'en', language: 'en' }, 83 | { code: 'zh_cn', language: 'zh-CN' }, 84 | { code: 'zh_tw', language: 'zh-TW' }, 85 | { code: 'ru', language: 'ru' }, 86 | ], 87 | detectBrowserLanguage: { 88 | useCookie: true, 89 | cookieKey: 'i18n_redirected', 90 | redirectOn: 'root', 91 | }, 92 | baseUrl: 'https://redenmc.com', 93 | }, 94 | vite: { 95 | vue: { 96 | template: { 97 | transformAssetUrls, 98 | }, 99 | }, 100 | server: { 101 | proxy: { 102 | '/api': 'https://api.redenmc.com/', 103 | }, 104 | }, 105 | }, 106 | features: { 107 | inlineStyles: false, 108 | }, 109 | experimental: { 110 | inlineRouteRules: true, 111 | }, 112 | // typescript: { 113 | // includeWorkspace: true, 114 | // }, 115 | nitro: { 116 | prerender: { 117 | failOnError: false, 118 | routes: [ 119 | '/', 120 | '/sitemap.xml', 121 | // '/opensearch.xml' 122 | ], 123 | ignore: [ 124 | '/api', 125 | '/__pinceau_tokens_config.json', 126 | '/__pinceau_tokens_schema.json', 127 | ], 128 | }, 129 | }, 130 | routeRules: { 131 | // '/api/search': { 132 | // prerender: true, 133 | // cache: {}, 134 | // }, 135 | '/api/**': { 136 | proxy: 137 | // process.env.NODE_ENV === 'development' || 138 | isPrerender 139 | ? 'https://api.redenmc.com/api/**' 140 | : 'http://localhost:10005/api/**', 141 | }, 142 | }, 143 | sitemap: { 144 | exclude: ['/secret/**', '/admin/**', '/api/**'], 145 | urls: async () => { 146 | const backendData: string[] = await ( 147 | await fetch( 148 | 'https://api.redenmc.com/api/mc-services/yisibite/all-internal', 149 | ) 150 | ).json(); 151 | return backendData.map((id) => ({ 152 | loc: `/litematica/${id}`, 153 | _i18nTransform: true, 154 | })); 155 | }, 156 | }, 157 | devServer: { 158 | host: '0.0.0.0', 159 | }, 160 | site: { 161 | url: 'redenmc.com', 162 | }, 163 | app: { 164 | pageTransition: { 165 | name: 'page', 166 | mode: 'out-in', // default 167 | }, 168 | }, 169 | pwa: { 170 | registerType: 'autoUpdate', 171 | devOptions: { 172 | // https://github.com/vite-pwa/nuxt/issues/77 173 | enabled: false, 174 | }, 175 | manifest: { 176 | name: 'Reden', 177 | short_name: 'Reden', 178 | icons: [ 179 | { 180 | src: '/reden_256.png', 181 | purpose: 'any maskable', 182 | sizes: '256x256', 183 | }, 184 | ], 185 | theme_color: '#ffffff', 186 | }, 187 | }, 188 | }); 189 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "dev-prod-backend": "nuxt dev --prod-backend", 9 | "generate": "nuxt generate", 10 | "preview": "nuxt preview", 11 | "postinstall": "nuxt prepare && node script/init-git-hash.js" 12 | }, 13 | "dependencies": { 14 | "@nuxt/content": "^2.13.4", 15 | "@nuxtjs/color-mode": "^3.5.2", 16 | "@nuxtjs/i18n": "^8.5.2", 17 | "@nuxtjs/mdc": "^0.12.1", 18 | "@pinia/nuxt": "^0.5.4", 19 | "@vite-pwa/nuxt": "^0.10.6", 20 | "@vueuse/core": "^10.9.0", 21 | "@vueuse/router": "^12.8.2", 22 | "ant-design-vue": "4.x", 23 | "core-js": "^3.29.0", 24 | "countries-and-timezones": "^3.6.0", 25 | "deepslate": "^0.23.5", 26 | "localforage": "^1.10.0", 27 | "nuxt": "^3.13.0", 28 | "randomcolor": "^0.6.2", 29 | "roboto-fontface": "*", 30 | "ua-parser-js": "^2.0.0", 31 | "vue": "^3.5.13", 32 | "vue-i18n": "^9.6.5", 33 | "vue-router": "^4.5.0", 34 | "vue-sonner": "^1.0.1", 35 | "vue-turnstile": "^1.0.8", 36 | "vue3-touch-events": "^4.2.0", 37 | "vuetify": "^3.7.2", 38 | "vuetify-nuxt-module": "^0.18.2", 39 | "vuetify-sonner": "^0.3.2" 40 | }, 41 | "devDependencies": { 42 | "@ant-design-vue/nuxt": "^1.4.6", 43 | "@mdi/font": "^7.4.47", 44 | "@nuxt/kit": "^3.1.50", 45 | "@nuxtjs/sitemap": "^6.0.0", 46 | "@types/randomcolor": "^0.5.9", 47 | "prettier": "3.2.5", 48 | "vite-plugin-vuetify": "^2.0.4" 49 | }, 50 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 51 | } 52 | -------------------------------------------------------------------------------- /pages/@[username].vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 76 | -------------------------------------------------------------------------------- /pages/admin/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /pages/admin/security-logs.vue: -------------------------------------------------------------------------------- 1 | 81 | 82 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /pages/admin/users.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 170 | 171 | 185 | -------------------------------------------------------------------------------- /pages/community-guidelines.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /pages/dmca.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /pages/docs/[...slug].vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /pages/download.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /pages/empire/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pages/empire/tutorial.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /pages/feature/debugger.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /pages/feature/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /pages/feature/rvc.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 70 | 71 | 95 | -------------------------------------------------------------------------------- /pages/feature/undo.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 79 | 80 | 102 | -------------------------------------------------------------------------------- /pages/forgot-password.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /pages/litematica/edit.vue: -------------------------------------------------------------------------------- 1 | 5 | 20 | 21 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /pages/litematica/review.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /pages/login.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 161 | 162 | 180 | -------------------------------------------------------------------------------- /pages/miku.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /pages/preview.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /pages/register.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 206 | 207 | 226 | -------------------------------------------------------------------------------- /pages/reset-password.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 77 | -------------------------------------------------------------------------------- /pages/sponsors.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 89 | -------------------------------------------------------------------------------- /pages/x-backup/me.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /pages/x-backup/plans.vue: -------------------------------------------------------------------------------- 1 | 99 | 100 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /plugins/icons/Bilibili.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /plugins/icons/CubeScan.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | -------------------------------------------------------------------------------- /plugins/icons/DiscordIcon.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /plugins/icons/Microsoft.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /plugins/icons/ModrinthFull.vue: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /plugins/icons/QuarkCloud.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /plugins/icons/YouTube_Logo_2017.vue: -------------------------------------------------------------------------------- 1 | 54 | -------------------------------------------------------------------------------- /plugins/icons/ZipArchive.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | -------------------------------------------------------------------------------- /plugins/icons/customSvgs.ts: -------------------------------------------------------------------------------- 1 | import { type DefineComponent, h } from 'vue'; 2 | import type { IconProps, IconSet } from 'vuetify'; 3 | import DiscordIcon from './DiscordIcon.vue'; 4 | import ModrinthFull from './ModrinthFull.vue'; 5 | import Bilibili from './Bilibili.vue'; 6 | import QuarkCloud from './QuarkCloud.vue'; 7 | import ZipArchive from './ZipArchive.vue'; 8 | import CubeScan from './CubeScan.vue'; 9 | import YouTube_Logo_2017 from '~/plugins/icons/YouTube_Logo_2017.vue'; 10 | import Google from './google.vue'; 11 | import Microsoft from './Microsoft.vue'; 12 | 13 | const customSvgNameToComponent: Record> = { 14 | DiscordIcon, 15 | ModrinthFull, 16 | Bilibili, 17 | QuarkCloud, 18 | ZipArchive, 19 | CubeScan, 20 | YouTube_Logo_2017, 21 | Google, 22 | Microsoft, 23 | }; 24 | 25 | export const customSVGs: IconSet = { 26 | component: (props: IconProps) => 27 | h(customSvgNameToComponent[props.icon as string]), 28 | }; 29 | -------------------------------------------------------------------------------- /plugins/icons/google.vue: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | // import this after install `@mdi/font` package 2 | // import '@mdi/font/css/materialdesignicons.css'; 3 | import 'vuetify/styles'; 4 | import { createVuetify } from 'vuetify'; 5 | import * as components from 'vuetify/components'; 6 | import { customSVGs } from '~/plugins/icons/customSvgs'; 7 | import { mdi } from 'vuetify/iconsets/mdi'; 8 | 9 | export default defineNuxtPlugin((app) => { 10 | const vuetify = createVuetify({ 11 | ssr: true, 12 | locale: { 13 | // adapter: createVueI18nAdapter({ i18n, useI18n }), 14 | }, 15 | icons: { 16 | defaultSet: 'mdi', 17 | sets: { 18 | mdi, 19 | custom: customSVGs, 20 | }, 21 | }, 22 | components: { 23 | ...components, 24 | }, 25 | theme: { 26 | themes: { 27 | /** 28 | * theme: light app: dark cookie: dark 29 | * background #121212 surface #212121 surface-bright #ccbfd6 surface-light #424242 surface-variant #a3a3a3 on-surface-variant #424242 primary #2196F3 primary-darken-1 #277CC1 secondary #54B6B2 secondary-darken-1 #48A9A6 error #CF6679 info #2196F3 success #4CAF50 warning #FB8C00 30 | */ 31 | dark: { 32 | dark: true, 33 | colors: { 34 | secondary: '#fb7299', 35 | }, 36 | }, 37 | light: { 38 | dark: false, 39 | colors: { 40 | secondary: '#fb7299', 41 | }, 42 | }, 43 | }, 44 | }, 45 | }); 46 | app.vueApp.use(vuetify); 47 | }); 48 | -------------------------------------------------------------------------------- /public/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-4957320708232906, DIRECT, f08c47fec0942fa0 2 | -------------------------------------------------------------------------------- /public/cae43db142664664989d50b7c690d397.txt: -------------------------------------------------------------------------------- 1 | cae43db142664664989d50b7c690d397 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/favicon.ico -------------------------------------------------------------------------------- /public/image/generals/city.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/generals/city.png -------------------------------------------------------------------------------- /public/image/generals/crown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/generals/crown.png -------------------------------------------------------------------------------- /public/image/generals/mountain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/generals/mountain.png -------------------------------------------------------------------------------- /public/image/generals/obstacle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/generals/obstacle.png -------------------------------------------------------------------------------- /public/image/generals/swamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/generals/swamp.png -------------------------------------------------------------------------------- /public/image/homepage/section/lever.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/lever.png -------------------------------------------------------------------------------- /public/image/homepage/section/lever_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/lever_base.png -------------------------------------------------------------------------------- /public/image/homepage/section/piston_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/piston_base.png -------------------------------------------------------------------------------- /public/image/homepage/section/piston_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/piston_head.png -------------------------------------------------------------------------------- /public/image/homepage/section/piston_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/piston_side.png -------------------------------------------------------------------------------- /public/image/homepage/section/redstone_dust_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/redstone_dust_0.png -------------------------------------------------------------------------------- /public/image/homepage/section/redstone_dust_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/redstone_dust_15.png -------------------------------------------------------------------------------- /public/image/homepage/section/redstone_lamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/redstone_lamp.png -------------------------------------------------------------------------------- /public/image/homepage/section/redstone_lamp_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/section/redstone_lamp_on.png -------------------------------------------------------------------------------- /public/image/homepage/undo/TNT.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/undo/TNT.webp -------------------------------------------------------------------------------- /public/image/homepage/undo/chest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/undo/chest.png -------------------------------------------------------------------------------- /public/image/homepage/undo/creeper.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/undo/creeper.webp -------------------------------------------------------------------------------- /public/image/homepage/undo/grass_block.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/undo/grass_block.webp -------------------------------------------------------------------------------- /public/image/homepage/undo/villager.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/homepage/undo/villager.webp -------------------------------------------------------------------------------- /public/image/reden-alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/reden-alipay.png -------------------------------------------------------------------------------- /public/image/sumimasen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/image/sumimasen.png -------------------------------------------------------------------------------- /public/litematica/atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/litematica/atlas.png -------------------------------------------------------------------------------- /public/reden_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/reden_256.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /api/ 3 | Disallow: /admin/ 4 | Disallow: *.mp3 5 | Disallow: *.mp4 6 | Disallow: *.woff 7 | Disallow: *.ttf 8 | Disallow: *.eot 9 | Disallow: *.woff2 10 | Disallow: /*?q=* 11 | -------------------------------------------------------------------------------- /public/video/nether_portal.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/public/video/nether_portal.mp4 -------------------------------------------------------------------------------- /script/fix-i18n.js: -------------------------------------------------------------------------------- 1 | // read from ./i18n/zh-CN.json, remove all empty values, and write to ./i18n/zh-CN.json 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import { fileURLToPath } from 'url'; 6 | 7 | const scriptPath = fileURLToPath(import.meta.url); 8 | 9 | function fixI18n(lang) { 10 | const i18nPath = path.resolve(scriptPath, `../../i18n/${lang}.json`); 11 | const i18n = JSON.parse(fs.readFileSync(i18nPath, 'utf8')); 12 | 13 | function removeEmptyValues(obj) { 14 | if (typeof obj !== 'object') { 15 | return obj; 16 | } 17 | const sorted = {}; 18 | Object.keys(obj) 19 | .toSorted() 20 | .forEach((key) => { 21 | const value = removeEmptyValues(obj[key]); 22 | if (typeof value === 'object') { 23 | if (value && Object.keys(value).length === 0) { 24 | return; 25 | } 26 | } else if (value === '') { 27 | return; 28 | } 29 | sorted[key] = value; 30 | }); 31 | return sorted; 32 | } 33 | 34 | // leave a blank line at the end 35 | fs.writeFileSync( 36 | i18nPath, 37 | JSON.stringify(removeEmptyValues(i18n), null, 2) + '\n', 38 | ); 39 | } 40 | 41 | fixI18n('zh_CN'); 42 | fixI18n('zh_TW'); 43 | fixI18n('ru_RU'); 44 | fixI18n('en'); 45 | -------------------------------------------------------------------------------- /script/init-git-hash.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { execSync } from 'node:child_process'; 3 | 4 | const hash = execSync('git rev-parse --short HEAD').toString().trim(); 5 | fs.writeFileSync('assets/hash.json', `"${hash}-dev"`); 6 | console.log('🎉[Reden] Initialized version info'); 7 | // write git commit hook 8 | fs.writeFileSync( 9 | '.git/hooks/pre-commit', 10 | `#!/bin/sh 11 | env node script/fix-i18n.js 12 | `, 13 | ); 14 | execSync('chmod +x .git/hooks/pre-commit'); 15 | console.log('🎉[Reden] Initialized pre-commit hook'); 16 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /store/app.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { defineStore } from 'pinia'; 3 | import { doFetchGet, type Profile } from '@/utils/constants'; 4 | import { useLocalStorage } from '@vueuse/core'; 5 | 6 | type AppState = { 7 | logined: boolean; 8 | username?: string; 9 | uid: number; 10 | csrfToken: string | null; 11 | userCache?: Profile; 12 | gads?: boolean; 13 | invertPreview: boolean; 14 | _isInChina?: boolean; 15 | theme: 'light' | 'dark'; 16 | }; 17 | 18 | export const storage = useLocalStorage('redenCache', { 19 | logined: false, 20 | username: undefined, 21 | uid: -1, 22 | csrfToken: null, 23 | userCache: undefined, 24 | gads: true, 25 | invertPreview: false, 26 | _isInChina: undefined, 27 | theme: 'light', 28 | }); 29 | 30 | export const useAppStore = defineStore('reden', { 31 | state: () => 32 | ({ 33 | logined: false, 34 | username: undefined, 35 | uid: -1, 36 | csrfToken: null, 37 | userCache: undefined, 38 | gads: true, 39 | invertPreview: false, 40 | theme: 'light', 41 | }) as AppState, 42 | hydrate(storeState, initialState) { 43 | console.log( 44 | '[pinia] hydrate', 45 | JSON.stringify(storeState), 46 | JSON.stringify(storage.value), 47 | ); 48 | storeState.logined = storage.value.logined; 49 | storeState.username = storage.value.username; 50 | storeState.uid = storage.value.uid; 51 | storeState.csrfToken = storage.value.csrfToken; 52 | storeState.userCache = storage.value.userCache; 53 | storeState.theme = storage.value.theme ?? 'light'; 54 | storeState._isInChina = storage.value._isInChina ?? undefined; 55 | }, 56 | actions: { 57 | save() { 58 | storage.value = { 59 | logined: this.logined, 60 | username: this.username, 61 | uid: this.uid, 62 | csrfToken: this.csrfToken, 63 | userCache: this.userCache, 64 | gads: this.gads, 65 | invertPreview: this.invertPreview, 66 | theme: this.theme, 67 | _isInChina: this._isInChina, 68 | }; 69 | }, 70 | login(username: string, uid: number) { 71 | this.logined = true; 72 | this.username = username; 73 | this.uid = uid; 74 | this.save(); 75 | }, 76 | updateCache(profile: Profile) { 77 | this.logined = true; 78 | this.uid = profile.id; 79 | this.userCache = profile; 80 | this.username = profile.username; 81 | this.save(); 82 | }, 83 | setCsrfToken(token: string) { 84 | this.logined = true; 85 | this.csrfToken = token; 86 | this.save(); 87 | }, 88 | toggleInvertPreview() { 89 | this.invertPreview = !this.invertPreview; 90 | this.save(); 91 | }, 92 | async isInChina() { 93 | if (this._isInChina !== undefined) return this._isInChina; 94 | if (!import.meta.client) return false; 95 | else { 96 | const res = await doFetchGet('/api/ip'); 97 | if (res.ok) { 98 | const data: { 99 | ip: string; 100 | mm?: { 101 | country_code: string; 102 | country: string; 103 | country_zh: string; 104 | subdivision: string; 105 | subdivision_zh: string; 106 | city: string; 107 | city_zh: string; 108 | latitude: number; 109 | longitude: number; 110 | }; 111 | } = await res.json(); 112 | if (data.mm?.country_code === 'CN') { 113 | console.log('ip', data.ip, 'is in china.'); 114 | this._isInChina = true; 115 | this.save(); 116 | return true; 117 | } else if (data.mm?.country_code) { 118 | this._isInChina = false; 119 | this.save(); 120 | return false; 121 | } else { 122 | console.log('ip', data.ip, 'is unknown.'); 123 | } 124 | } 125 | } 126 | }, 127 | logout() { 128 | this.logined = false; 129 | this.username = undefined; 130 | this.uid = -1; 131 | this.csrfToken = null; 132 | this.userCache = undefined; 133 | this._isInChina = undefined; 134 | this.save(); 135 | }, 136 | }, 137 | }); 138 | -------------------------------------------------------------------------------- /store/message.ts: -------------------------------------------------------------------------------- 1 | // stores/message.ts 2 | import { defineStore } from 'pinia'; 3 | 4 | export const useMessageStore = defineStore('message', { 5 | state: (): { 6 | unreadCount: number; 7 | drawer: boolean; 8 | } => ({ 9 | // TODO 初始化未读消息数量 10 | unreadCount: 3, 11 | drawer: false, 12 | }), 13 | actions: { 14 | decrementUnreadCount() { 15 | if (this.unreadCount > 0) { 16 | console.log(this.unreadCount); 17 | this.unreadCount--; 18 | } 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /store/meta.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { doFetchGet } from '@/utils/constants'; 3 | 4 | const name = 'meta'; 5 | 6 | export type Meta = { 7 | developmentMode: boolean; 8 | baseUrl: string; 9 | production: boolean; 10 | gitHash?: string; 11 | gitBranch?: string; 12 | buildTime?: string; 13 | buildTimestamp?: number; 14 | }; 15 | 16 | function state(): Meta { 17 | if (import.meta.client) { 18 | const data = localStorage.getItem(name); 19 | if (data) { 20 | return JSON.parse(data); 21 | } 22 | } 23 | let url = useRequestURL(); 24 | return { 25 | developmentMode: import.meta.dev, 26 | baseUrl: url.host, 27 | production: !import.meta.dev, 28 | }; 29 | } 30 | 31 | export const useBackendMeta = defineStore(name, { 32 | state, 33 | actions: { 34 | refresh() { 35 | doFetchGet('/api/meta') 36 | .then((res) => res.json()) 37 | .then((data: Meta) => { 38 | localStorage.setItem(name, JSON.stringify(data)); 39 | this.developmentMode = data.developmentMode; 40 | this.baseUrl = data.baseUrl; 41 | this.production = data.production; 42 | this.gitHash = data.gitHash; 43 | this.gitBranch = data.gitBranch; 44 | this.buildTime = data.buildTime; 45 | this.buildTimestamp = data.buildTimestamp; 46 | }); 47 | }, 48 | get() { 49 | if (!this.gitHash) this.refresh(); 50 | return this; 51 | }, 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json", 4 | "compilerOptions": { 5 | "types": ["@nuxt/types", "@nuxtjs/i18n"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /utils/api.ts: -------------------------------------------------------------------------------- 1 | export const useFetchUserGet = (nameOrId: number | string) => 2 | useFetch(`/api/users/${nameOrId}`); 3 | -------------------------------------------------------------------------------- /utils/conditionParser.ts: -------------------------------------------------------------------------------- 1 | export type Condition = (value: number) => boolean | string; 2 | 3 | export function parseCondition( 4 | conditionString: string, 5 | t: (key: string, params?: any) => string, 6 | ): Condition { 7 | const minMatch = conditionString.match(/min\((\d+)\)/); 8 | if (minMatch) { 9 | const minValue = parseInt(minMatch[1], 10); 10 | const func = (value: number) => 11 | value >= minValue || 12 | t('litematica_generator.size_min', { size: minValue }); 13 | func.min = minValue; 14 | return func; 15 | } 16 | 17 | const maxMatch = conditionString.match(/max\((\d+)\)/); 18 | if (maxMatch) { 19 | const maxValue = parseInt(maxMatch[1], 10); 20 | const func = (value: number) => 21 | value <= maxValue || 22 | t('litematica_generator.size_max', { size: maxValue }); 23 | func.max = maxValue; 24 | return func; 25 | } 26 | 27 | const modMatch = conditionString.match(/mod\((\d+),(\d+)\)/); 28 | if (modMatch) { 29 | const modValue = parseInt(modMatch[1], 10); 30 | const remValue = parseInt(modMatch[2], 10); 31 | return (value: number) => 32 | value % modValue === remValue || 33 | t('litematica_generator.size_mod', { 34 | mod: modValue, 35 | rem: remValue, 36 | }); 37 | } 38 | 39 | // Default checker 40 | return (value: number) => true; // Always return true if condition not matched. 41 | } 42 | -------------------------------------------------------------------------------- /utils/litematic-utils.ts: -------------------------------------------------------------------------------- 1 | import { NbtType } from 'deepslate'; 2 | 3 | export class Litematic { 4 | public regions: LitematicRegion[]; 5 | 6 | constructor() { 7 | this.regions = []; 8 | } 9 | } 10 | 11 | export class LitematicRegion { 12 | public x: number; 13 | public y: number; 14 | public z: number; 15 | public width: number; 16 | public height: number; 17 | public depth: number; 18 | public blockPalette: any; 19 | public blocks: any[][][]; 20 | 21 | constructor( 22 | x: number, 23 | y: number, 24 | z: number, 25 | width: number, 26 | height: number, 27 | depth: number, 28 | ) { 29 | this.x = x; 30 | this.y = y; 31 | this.z = z; 32 | this.width = width; 33 | this.height = height; 34 | this.depth = depth; 35 | this.blocks = []; 36 | } 37 | } 38 | 39 | export function readLitematicFromNBTData(nbtdata: any): Litematic { 40 | // Get rid of all the annoying stuff basically 41 | 42 | const litematica = new Litematic(); 43 | const regions = nbtdata.root.Regions.value; 44 | for (let regionName in regions) { 45 | const region = regions[regionName].value; 46 | 47 | const blockPalette = __stripNBTTyping(region.BlockStatePalette); 48 | 49 | // Find the minimum number of bits needed to express all blocks 50 | const nbits = Math.ceil(Math.log2(blockPalette.length)); 51 | 52 | const x = region.Position.value.x.value as number; 53 | const y = region.Position.value.y.value as number; 54 | const z = region.Position.value.z.value as number; 55 | const width = region.Size.value.x.value as number; 56 | const height = region.Size.value.y.value as number; 57 | const depth = region.Size.value.z.value as number; 58 | 59 | let blockData = region.BlockStates.value; 60 | 61 | let blocks = processNBTRegionData(blockData, nbits, width, height, depth); 62 | 63 | let litematicRegion = new LitematicRegion(x, y, z, width, height, depth); 64 | litematicRegion.blocks = blocks; 65 | litematicRegion.blockPalette = blockPalette; 66 | 67 | litematica.regions.push(litematicRegion); 68 | } 69 | 70 | return litematica; 71 | } 72 | 73 | /** 74 | * 75 | * @param regionData nbt: longs[] 76 | * @param nbits 77 | * @param width 78 | * @param height 79 | * @param depth 80 | */ 81 | function processNBTRegionData( 82 | regionData: [number, number][], 83 | nbits: number, 84 | width: number, 85 | height: number, 86 | depth: number, 87 | ) { 88 | // Function to take the raw array and convert it into a 3D array 89 | // The raw data is a list of nbits-wide numbers all packed together into a single array of 64-bit* ints 90 | // I ripped off some python code for this, can't remember where from. 91 | // (* of course this is javascript so each int is split into an array fo 2 32-bit ints) 92 | 93 | let mask = (1 << nbits) - 1; 94 | 95 | let y_shift = Math.abs(width * depth); 96 | let z_shift = Math.abs(width); 97 | const blocks: any[][][] = []; 98 | for (let x = 0; x < Math.abs(width); x++) { 99 | blocks[x] = []; 100 | for (let y = 0; y < Math.abs(height); y++) { 101 | blocks[x][y] = []; 102 | for (let z = 0; z < Math.abs(depth); z++) { 103 | let index = y * y_shift + z * z_shift + x; 104 | 105 | let start_offset = index * nbits; 106 | 107 | let start_arr_index = start_offset >>> 5; /// divide by 32 108 | let end_arr_index = ((index + 1) * nbits - 1) >>> 5; 109 | let start_bit_offset = start_offset & 0x1f; // % 32 110 | 111 | // This bit here is to handle the fact that the 64 bit numbers have to be broken down to 112 | // 32bit numbers in javascript. 113 | let half_ind = start_arr_index >>> 1; 114 | let blockStart; 115 | let blockEnd; 116 | if ((start_arr_index & 0x1) == 0) { 117 | blockStart = regionData[half_ind][1]; 118 | blockEnd = regionData[half_ind][0]; 119 | } else { 120 | blockStart = regionData[half_ind][0]; 121 | if (half_ind + 1 < regionData.length) { 122 | blockEnd = regionData[half_ind + 1][1]; 123 | } else { 124 | // It seems that sometimes the index can extend past the end of the array, but this fix works (for now) 125 | blockEnd = 0x0; 126 | } 127 | } 128 | 129 | if (start_arr_index == end_arr_index) { 130 | blocks[x][y][z] = (blockStart >>> start_bit_offset) & mask; 131 | } else { 132 | let end_offset = 32 - start_bit_offset; // num curtailed bits 133 | blocks[x][y][z] = 134 | ((blockStart >>> start_bit_offset) & mask) | 135 | ((blockEnd << end_offset) & mask); // & mask; 136 | } 137 | } 138 | } 139 | } 140 | return blocks; 141 | } 142 | 143 | /** 144 | * Hacky function needed to convert NBT to pure JSON 145 | * use at your own risk 146 | * 147 | * edited for new deepslate NBG implementation 148 | * exercise extreme caution 149 | * @param nbtData 150 | */ 151 | export function __stripNBTTyping(nbtData: any): any { 152 | if (nbtData.hasOwnProperty('type')) { 153 | switch (NbtType[nbtData.type]) { 154 | case 'Compound': 155 | const newDict: Record = {}; 156 | for (const [k, v] of Object.entries(nbtData.value)) { 157 | newDict[k] = __stripNBTTyping(v); 158 | } 159 | return newDict; 160 | case 'List': 161 | const newList: Record = []; 162 | for (const [k, v] of Object.entries(nbtData.value.items)) { 163 | newList[k] = __stripNBTTyping(v); 164 | } 165 | return newList; 166 | default: 167 | return nbtData.value; 168 | } 169 | } else { 170 | switch (nbtData.constructor) { 171 | case Object: 172 | const newDict: Record = {}; 173 | for (const [k, v] of Object.entries(nbtData)) { 174 | newDict[k] = __stripNBTTyping(v); 175 | } 176 | return newDict; 177 | default: 178 | return nbtData; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /utils/litematica/atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedenMC/reden-website/8fb95522717ed8ff8ce46ea046acc20c433d7542/utils/litematica/atlas.png --------------------------------------------------------------------------------