Compare commits
8 Commits
v2026.2.1-
...
v2026.2.2-
| Author | SHA1 | Date | |
|---|---|---|---|
| 247f437445 | |||
| 0e94367223 | |||
| a9ee28b395 | |||
| bd074c5c9d | |||
| 42c552c528 | |||
| 3b376e5386 | |||
| 45a8e6b4a1 | |||
| 1f8e9c3c56 |
32
.gitchangelog.rc
Normal file
32
.gitchangelog.rc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
ignore_regexps = [
|
||||||
|
r'@minor', r'!minor',
|
||||||
|
r'@cosmetic', r'!cosmetic',
|
||||||
|
r'@refactor', r'!refactor',
|
||||||
|
r'@wip', r'!wip',
|
||||||
|
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
|
||||||
|
r'^$', ## ignore commits with empty messages
|
||||||
|
r'@skipChangelog', r'!skipChangelog', r'skipChangeLog', r'!skipChangeLog',
|
||||||
|
r'Merge branch', r'Merge remote-tracking branch', r'!deploy',
|
||||||
|
]
|
||||||
|
section_regexps = [
|
||||||
|
('New', [
|
||||||
|
r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Changes', [
|
||||||
|
r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Fix', [
|
||||||
|
r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
|
||||||
|
]),
|
||||||
|
('Other', None ## Match all lines
|
||||||
|
),
|
||||||
|
]
|
||||||
|
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
|
||||||
|
subject_process = (strip |
|
||||||
|
ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
|
||||||
|
SetIfEmpty("No commit message.") | ucfirst | final_dot)
|
||||||
|
tag_filter_regexp = r'^(v)?[0-9]+\.[0-9]+(\.[0-9]+)?(\-[0-9]+)?$'
|
||||||
|
unreleased_version_label = "(unreleased)"
|
||||||
|
output_engine = mustache("markdown")
|
||||||
|
include_merge = True
|
||||||
|
revs = []
|
||||||
568
CHANGELOG.md
568
CHANGELOG.md
@@ -1,200 +1,390 @@
|
|||||||
Changelog
|
# Changelog
|
||||||
=========
|
|
||||||
|
|
||||||
|
|
||||||
(unreleased)
|
## v2026.2.1-8 (2026-04-18)
|
||||||
------------
|
|
||||||
|
|
||||||
New
|
### Fix
|
||||||
~~~
|
|
||||||
- Add notification email when a user is registered #4. [Lang]
|
|
||||||
- Add Contact page with email sending behaviour #4. [Lang]
|
|
||||||
- Add timer for the acceptance of the challenge #4. [Lang]
|
|
||||||
- Registered users have avatars next to the timer #4. [Lang]
|
|
||||||
- Add opportunity to use profile picture. #4. [Lang]
|
|
||||||
- Add more stats and a dialog for the recent battle that can be
|
|
||||||
shareable #4. [Lang]
|
|
||||||
- Implement the 2FA authentication (TOTP and backup codes) #4. [Lang]
|
|
||||||
- Add beta logo to the corner #3. [Lang]
|
|
||||||
- Add mineseeker game to the symfony 4 project #3. [Lang]
|
|
||||||
- Upgrade to the latest symfony v4 #3. [Lang]
|
|
||||||
|
|
||||||
Changes
|
* Quickfix for https-only login - & add user data when the user is not logged in #4. [Lang]
|
||||||
~~~~~~~
|
|
||||||
- Add notification on activation too #4. [Lang]
|
|
||||||
- Change the shareable battle - add avatars to it - even on the og tags
|
|
||||||
#4. [Lang]
|
|
||||||
- Change text #4. [Lang]
|
|
||||||
- Add donation button #4. [Lang]
|
|
||||||
- Protect the gameplay with recaptcha #4. [Lang]
|
|
||||||
- The waiting dialog is uncloseable until the time is up #4. [Lang]
|
|
||||||
- Add share button to the overlay when the game ends #4. [Lang]
|
|
||||||
- Make fancy og tags - and create a special one for battle sharing #4.
|
|
||||||
[Lang]
|
|
||||||
- The user's avatar will be saved as a uuid.extension #4. [Lang]
|
|
||||||
- Fix missing favicon #4. [Lang]
|
|
||||||
- Add modern Webauthn authentication #4. [Lang]
|
|
||||||
- Refactor all forms to have Symfony Form Types & Validation
|
|
||||||
Constrainsts - & implement Google ReCapthca v3 #4. [Lang]
|
|
||||||
- Add forgot password functionality #4. [Lang]
|
|
||||||
- Increase the minimum PHP version to the latest major - and massive
|
|
||||||
refactor on back-end, like Controllers and Repositories #4. [Lang]
|
|
||||||
- Redesign the resign dialog #4. [Lang]
|
|
||||||
- Re-implement the waiting for opponent dialog - refactor its gfx - &
|
|
||||||
add online user selection dialog #4. [Lang]
|
|
||||||
- Improve the gfx on homepage - implement login/register and activation
|
|
||||||
for authentication - and add the first version of profile page #4.
|
|
||||||
[Lang]
|
|
||||||
- Refactor and redesign the gfx on front-end #4. [Lang]
|
|
||||||
- Add timers to each player - renew the whole migration #4. [Lang]
|
|
||||||
- Use namespaces for front-end #4. [Lang]
|
|
||||||
- Replace webpack w/ vite & remove old, legacy jQuery from the code #4.
|
|
||||||
[Lang]
|
|
||||||
- More, massive refactor for front-end #4. [Lang]
|
|
||||||
- Massive refactor on front-end - and remove unnecessary deps #4. [Lang]
|
|
||||||
- Change the code style to fit the current standard #4. [Lang]
|
|
||||||
- Refactor to use Attributes instead of yaml markdown #4. [Lang]
|
|
||||||
- Outsource the Grid generation and interactions to the backend #4.
|
|
||||||
[Lang]
|
|
||||||
- Remove unnecessary variables and prune the Facebook registration
|
|
||||||
method #4. [Lang]
|
|
||||||
- Replace the legacy gos/web-socket-bundle & replace it with Mercure
|
|
||||||
protocol #4. [Lang]
|
|
||||||
- Created the first working solution since 7 yrs #4. [Lang]
|
|
||||||
- Make the first working version - the stepping is broken due to the
|
|
||||||
algorythm structure #4. [Lang]
|
|
||||||
- Change the composer default php minimum environment #3. [Lang]
|
|
||||||
- Change the default url to wss on frontend #3. [Lang]
|
|
||||||
- Refactor Rpc and Topic classes #3. [Lang]
|
|
||||||
- Refactor classes and reformat some layout #3. [Lang]
|
|
||||||
- Remove deprecated files #3. [Lang]
|
|
||||||
- Doc in README.md #3. [Lang]
|
|
||||||
- Gitignore a js.map file #2. [Lang]
|
|
||||||
|
|
||||||
Fix
|
|
||||||
~~~
|
|
||||||
- The meta tags does not have https scheme - nothing worked in
|
|
||||||
configuration #4. [Lang]
|
|
||||||
- Another attempt to fix the email assets #4. [Lang]
|
|
||||||
- The images does not shows in emails #4. [Lang]
|
|
||||||
- Missing font-awesome icons on bare-metal environment #4. [Lang]
|
|
||||||
- Quickfix for email sending #4. [Lang]
|
|
||||||
|
|
||||||
Other
|
|
||||||
~~~~~
|
|
||||||
- Hg: pkg: new version release !skipChangelog. [Lang]
|
|
||||||
- Pkg: usr: solve the not-working mailing on dev env under docker #4.
|
|
||||||
[Lang]
|
|
||||||
- Deploy version 1.1.0 !deploy #11. [Lang]
|
|
||||||
|
|
||||||
|
|
||||||
1.1.0 (2019-10-26)
|
## v2026.2.1-7 (2026-04-16)
|
||||||
------------------
|
|
||||||
|
|
||||||
Changes
|
### Changes
|
||||||
~~~~~~~
|
|
||||||
- Reinit project - disable redis module and make the project compatible
|
* Add consent checkbox to user's registration - and fix the sharing pics #4. [Lang]
|
||||||
w/ PHP7.3 #2. [Lang]
|
|
||||||
|
* Add correct version numbering and CHANGELOG - and add the LICENSE #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
0.4.0 (2019-10-26)
|
## v2026.2.1-6 (2026-04-16)
|
||||||
------------------
|
|
||||||
- Change session driver to REDIS. [Lang]
|
### Changes
|
||||||
- Add created, updated field to db && improve graph design. [Lang]
|
|
||||||
- Cache setup && optimalize for google pagespeed && optimalize all
|
* Update all texts on all pages - extend them with the game specific things #4. [Lang]
|
||||||
images. [Lang]
|
|
||||||
- Improve graph design on homepage && add footer and techs && add
|
|
||||||
official pages. [Lang]
|
## v2026.2.1-5 (2026-04-16)
|
||||||
- Bugfix mine websocket periodic mysql calling. [Lang]
|
|
||||||
- Bugfix hwioauth remember me && centralize hwioauth and facebook
|
### Fix
|
||||||
settings. [Lang]
|
|
||||||
- Centralize jquery && bugfix mysql auto-termination problem w/ user
|
* The meta tags does not have https scheme - nothing worked in configuration #4. [Lang]
|
||||||
auth. [Lang]
|
|
||||||
- Release beta4. [Lang]
|
|
||||||
- Gitignore npm debug log. [Lang]
|
## v2026.2.1-4 (2026-04-15)
|
||||||
- Add english lang everywhere && add snowfall && add centralized version
|
|
||||||
nbr && improve stylesheet && slack integration. [Lang]
|
### New
|
||||||
- Bugfix #30 && random bg in game. [Lang]
|
|
||||||
- Add google analytics and facebook scripts && improve url share method
|
* Add notification email when a user is registered #4. [Lang]
|
||||||
w/ fb && enforce https in prod. [Lang]
|
|
||||||
- Reg and login buttons on index && remove list method && facebook
|
### Changes
|
||||||
centralize. [Lang]
|
|
||||||
- Redesign user frontend. [Lang]
|
* Add notification on activation too #4. [Lang]
|
||||||
- Mods for performance; one js.min file on prod. [Lang]
|
|
||||||
- Improve webpack config for prod compile #23. [Lang]
|
|
||||||
- Ssl handling #22 && reconnection issues #20, #21. [Lang]
|
## v2026.2.1-2 (2026-04-15)
|
||||||
- Facebook prod settings w/ app; hwi/HWIOAuthBundle. [Lang]
|
|
||||||
- Refact && game reconnection and restore w/o refresh #3 && bugfix bomb
|
### Fix
|
||||||
explosion on opponent mines #19. [Lang]
|
|
||||||
- Typo in rpc. [Lang]
|
* Another attempt to fix the email assets #4. [Lang]
|
||||||
- Handle prod mysql timeout && graphics improve. [Lang]
|
|
||||||
- Gitignore webpacked index.js. [Lang]
|
|
||||||
- Add production mods. [Lang]
|
## v2026.2.1-1 (2026-04-15)
|
||||||
- Bugfix points saving and exploded bombs to db && you can resign #6.
|
|
||||||
[Lang]
|
### Fix
|
||||||
- Bugfix resign button existence #11. [Lang]
|
|
||||||
- Bugfix opponent bomb btn buzz on hover #10. [Lang]
|
* The images does not shows in emails #4. [Lang]
|
||||||
- Bugfix points problem in the end #16. [Lang]
|
|
||||||
- Add desc to every user #9. [Lang]
|
|
||||||
- Clipboard - not working #8. [Lang]
|
## v2026.2.1-0 (2026-04-15)
|
||||||
- Random player on start #5. [Lang]
|
|
||||||
- Show left mines after end #2 && reduce network traffic && better
|
### New
|
||||||
active field checking method. [Lang]
|
|
||||||
- Some refactor #13. [Lang]
|
* Add Contact page with email sending behaviour #4. [Lang]
|
||||||
- Bugfix grid field render #12. [Lang]
|
|
||||||
- Game ends after x mines. [Lang]
|
### Changes
|
||||||
- Add new sounds && refactor && new bg images && form redesigns. [Lang]
|
|
||||||
- Bugfix entities gridrow, grid && improve graph design on homepage.
|
* Add missing .env variable and increase the version number and add missing data from front-end and back-end deps descriptor #4. [Lang]
|
||||||
[Lang]
|
|
||||||
- Some refactor && prod settings. [Lang]
|
* Change the shareable battle - add avatars to it - even on the og tags #4. [Lang]
|
||||||
- Improve graphics design in game. [Lang]
|
|
||||||
- Bugfix grid row in entity. [Lang]
|
* Change text #4. [Lang]
|
||||||
- Bugfix changePlayer after bomb explosion. [Lang]
|
|
||||||
- Improve game graph design. [Lang]
|
### Fix
|
||||||
- Login and register form more design. [Lang]
|
|
||||||
- Add basic design to userbundle && refactor. [Lang]
|
* The mailhog is crashed on development env #4. [Lang]
|
||||||
- Add font-awesome. [Lang]
|
|
||||||
- Working user authentication w/ fb and plain login. [Lang]
|
* The og tags did not have proper http schema - they should have https #4. [Lang]
|
||||||
- Add facebook login module, hwi/HWIOAuthBundle. [Lang]
|
|
||||||
- Login && register form overrided. [Lang]
|
|
||||||
- Js and config refactor. [Lang]
|
## v2026.2.0-5 (2026-04-14)
|
||||||
- Replace gridcol object to json array in db. [Lang]
|
|
||||||
- Refactor. [Lang]
|
### Changes
|
||||||
- Save steps and point info to db. [Lang]
|
|
||||||
- Save the step data to db. [Lang]
|
* Add donation button #4. [Lang]
|
||||||
- Renamed the acme to mineseeker && handle when the user connection has
|
|
||||||
been lost. [Lang]
|
|
||||||
- Add player names to UI. [Lang]
|
## v2026.2.0-4 (2026-04-14)
|
||||||
- Add overlay && game do not start until the opponent came. [Lang]
|
|
||||||
- Add base64 encryption to grid when it has been sended to server.
|
### New
|
||||||
[Lang]
|
|
||||||
- On click opponents bomb, you cannot target && refactor. [Lang]
|
* Add timer for the acceptance of the challenge #4. [Lang]
|
||||||
- Warning when player has been found more than 20 mines. [Lang]
|
|
||||||
- Bugfix center mine counter animation. [Lang]
|
### Changes
|
||||||
- The opponent is the next when bomb is exploded. [Lang]
|
|
||||||
- Current username checked && refactor && remove players in channel when
|
* Protect the gameplay with recaptcha #4. [Lang]
|
||||||
they are more than 2. [Lang]
|
|
||||||
- Send bomb info and use it on opponent. [Lang]
|
* The waiting dialog is uncloseable until the time is up #4. [Lang]
|
||||||
- Add sounds w/ howler. [Lang]
|
|
||||||
- Bugfix multiple empty fields w/ one click on opponent view. [Lang]
|
* Add share button to the overlay when the game ends #4. [Lang]
|
||||||
- Refact && remove sound and logging && bugfix BIGBUG - handleGridField
|
|
||||||
and showAppropriateFields sort order... [Lang]
|
* Make fancy og tags - and create a special one for battle sharing #4. [Lang]
|
||||||
- Create first working communication. [Lang]
|
|
||||||
- Create entities and repositories. [Lang]
|
### Fix
|
||||||
- Changed websocket default port && debug RPC. [Lang]
|
|
||||||
- Created working session and client handler w/ websocket. [Lang]
|
* Missing font-awesome icons on bare-metal environment #4. [Lang]
|
||||||
- Working websocket client and server w/o session handling and storage.
|
|
||||||
[Lang]
|
|
||||||
- Composer update. [Lang]
|
## v2026.2.0-3 (2026-04-14)
|
||||||
- Improve game && start sound creating. [Lang]
|
|
||||||
- Refactor grid control and grid field. [Lang]
|
### Changes
|
||||||
- Created basic game w/ table and animations. [Lang]
|
|
||||||
- Websocket basic setup FE & BE && working basic game w/ react &&
|
* The user's avatar will be saved as a uuid.extension #4. [Lang]
|
||||||
webpack & babel config. [Lang]
|
|
||||||
- Gitignore node_modules && add symlink to node_modules (just for
|
|
||||||
install) && basic react. [Lang]
|
## v2026.2.0-1 (2026-04-14)
|
||||||
- Add react hello world. [Lang]
|
|
||||||
- Rename project in config. [Lang]
|
### Fix
|
||||||
- Initial commit && create project in symfony3. [Lang]
|
|
||||||
|
* Quickfix for email sending #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## v2026.2.0-0 (2026-04-14)
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
* Registered users have avatars next to the timer #4. [Lang]
|
||||||
|
|
||||||
|
* Add opportunity to use profile picture. #4. [Lang]
|
||||||
|
|
||||||
|
* Add more stats and a dialog for the recent battle that can be shareable #4. [Lang]
|
||||||
|
|
||||||
|
* Implement the 2FA authentication (TOTP and backup codes) #4. [Lang]
|
||||||
|
|
||||||
|
* Add beta logo to the corner #3. [Lang]
|
||||||
|
|
||||||
|
* Add mineseeker game to the symfony 4 project #3. [Lang]
|
||||||
|
|
||||||
|
* Upgrade to the latest symfony v4 #3. [Lang]
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Implement CD script to Gitea and add docs to the process #4. [Lang]
|
||||||
|
|
||||||
|
* Remove unnecessary cdn based fonts #4. [Lang]
|
||||||
|
|
||||||
|
* Update docs #4. [Lang]
|
||||||
|
|
||||||
|
* Add JWT generation script to make Mercure safe #4. [Lang]
|
||||||
|
|
||||||
|
* Fix missing favicon #4. [Lang]
|
||||||
|
|
||||||
|
* Make compatible the whole project with bare metal AND with docker #4. [Lang]
|
||||||
|
|
||||||
|
* Add modern Webauthn authentication #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor all forms to have Symfony Form Types & Validation Constrainsts - & implement Google ReCapthca v3 #4. [Lang]
|
||||||
|
|
||||||
|
* Add forgot password functionality #4. [Lang]
|
||||||
|
|
||||||
|
* Increase the minimum PHP version to the latest major - and massive refactor on back-end, like Controllers and Repositories #4. [Lang]
|
||||||
|
|
||||||
|
* Redesign the resign dialog #4. [Lang]
|
||||||
|
|
||||||
|
* Re-implement the waiting for opponent dialog - refactor its gfx - & add online user selection dialog #4. [Lang]
|
||||||
|
|
||||||
|
* Improve the gfx on homepage - implement login/register and activation for authentication - and add the first version of profile page #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor and redesign the gfx on front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Upgrade to the latest LTS Symfony package and backend #4. [Lang]
|
||||||
|
|
||||||
|
* Add timers to each player - renew the whole migration #4. [Lang]
|
||||||
|
|
||||||
|
* Update the vite related stuff because CORS and React errors - reinit the miration #4. [Lang]
|
||||||
|
|
||||||
|
* Use namespaces for front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Replace webpack w/ vite & remove old, legacy jQuery from the code #4. [Lang]
|
||||||
|
|
||||||
|
* More, massive refactor for front-end #4. [Lang]
|
||||||
|
|
||||||
|
* Massive refactor on front-end - and remove unnecessary deps #4. [Lang]
|
||||||
|
|
||||||
|
* Change the code style to fit the current standard #4. [Lang]
|
||||||
|
|
||||||
|
* Refactor to use Attributes instead of yaml markdown #4. [Lang]
|
||||||
|
|
||||||
|
* Outsource the Grid generation and interactions to the backend #4. [Lang]
|
||||||
|
|
||||||
|
* Remove unnecessary variables and prune the Facebook registration method #4. [Lang]
|
||||||
|
|
||||||
|
* Replace the legacy gos/web-socket-bundle & replace it with Mercure protocol #4. [Lang]
|
||||||
|
|
||||||
|
* Make a massive refactor to the backend and remove all unnecessary deps - and make small refactors for the frontend too #4. [Lang]
|
||||||
|
|
||||||
|
* Created the first working solution since 7 yrs #4. [Lang]
|
||||||
|
|
||||||
|
* Add some changes on BE - add eslint and editorconfig - and add some deps #4. [Lang]
|
||||||
|
|
||||||
|
* Make the first working version - the stepping is broken due to the algorythm structure #4. [Lang]
|
||||||
|
|
||||||
|
* Change the composer default php minimum environment #3. [Lang]
|
||||||
|
|
||||||
|
* Change the default url to wss on frontend #3. [Lang]
|
||||||
|
|
||||||
|
* Refactor Rpc and Topic classes #3. [Lang]
|
||||||
|
|
||||||
|
* Refactor classes and reformat some layout #3. [Lang]
|
||||||
|
|
||||||
|
* Remove deprecated files #3. [Lang]
|
||||||
|
|
||||||
|
* Doc in README.md #3. [Lang]
|
||||||
|
|
||||||
|
* Gitignore a js.map file #2. [Lang]
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* Pkg: usr: solve the not-working mailing on dev env under docker #4. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1.0 (2019-10-26)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Reinit project - disable redis module and make the project compatible w/ PHP7.3 #2. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
## 0.4.0 (2019-10-26)
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
* Change session driver to REDIS. [Lang]
|
||||||
|
|
||||||
|
* Add created, updated field to db && improve graph design. [Lang]
|
||||||
|
|
||||||
|
* Cache setup && optimalize for google pagespeed && optimalize all images. [Lang]
|
||||||
|
|
||||||
|
* Improve graph design on homepage && add footer and techs && add official pages. [Lang]
|
||||||
|
|
||||||
|
* Bugfix mine websocket periodic mysql calling. [Lang]
|
||||||
|
|
||||||
|
* Bugfix hwioauth remember me && centralize hwioauth and facebook settings. [Lang]
|
||||||
|
|
||||||
|
* Centralize jquery && bugfix mysql auto-termination problem w/ user auth. [Lang]
|
||||||
|
|
||||||
|
* Release beta4. [Lang]
|
||||||
|
|
||||||
|
* Gitignore npm debug log. [Lang]
|
||||||
|
|
||||||
|
* Add english lang everywhere && add snowfall && add centralized version nbr && improve stylesheet && slack integration. [Lang]
|
||||||
|
|
||||||
|
* Bugfix #30 && random bg in game. [Lang]
|
||||||
|
|
||||||
|
* Add google analytics and facebook scripts && improve url share method w/ fb && enforce https in prod. [Lang]
|
||||||
|
|
||||||
|
* Reg and login buttons on index && remove list method && facebook centralize. [Lang]
|
||||||
|
|
||||||
|
* Redesign user frontend. [Lang]
|
||||||
|
|
||||||
|
* Mods for performance; one js.min file on prod. [Lang]
|
||||||
|
|
||||||
|
* Improve webpack config for prod compile #23. [Lang]
|
||||||
|
|
||||||
|
* Ssl handling #22 && reconnection issues #20, #21. [Lang]
|
||||||
|
|
||||||
|
* Facebook prod settings w/ app; hwi/HWIOAuthBundle. [Lang]
|
||||||
|
|
||||||
|
* Refact && game reconnection and restore w/o refresh #3 && bugfix bomb explosion on opponent mines #19. [Lang]
|
||||||
|
|
||||||
|
* Typo in rpc. [Lang]
|
||||||
|
|
||||||
|
* Handle prod mysql timeout && graphics improve. [Lang]
|
||||||
|
|
||||||
|
* Gitignore webpacked index.js. [Lang]
|
||||||
|
|
||||||
|
* Add production mods. [Lang]
|
||||||
|
|
||||||
|
* Bugfix points saving and exploded bombs to db && you can resign #6. [Lang]
|
||||||
|
|
||||||
|
* Bugfix resign button existence #11. [Lang]
|
||||||
|
|
||||||
|
* Bugfix opponent bomb btn buzz on hover #10. [Lang]
|
||||||
|
|
||||||
|
* Bugfix points problem in the end #16. [Lang]
|
||||||
|
|
||||||
|
* Add desc to every user #9. [Lang]
|
||||||
|
|
||||||
|
* Clipboard - not working #8. [Lang]
|
||||||
|
|
||||||
|
* Random player on start #5. [Lang]
|
||||||
|
|
||||||
|
* Show left mines after end #2 && reduce network traffic && better active field checking method. [Lang]
|
||||||
|
|
||||||
|
* Some refactor #13. [Lang]
|
||||||
|
|
||||||
|
* Bugfix grid field render #12. [Lang]
|
||||||
|
|
||||||
|
* Game ends after x mines. [Lang]
|
||||||
|
|
||||||
|
* Add new sounds && refactor && new bg images && form redesigns. [Lang]
|
||||||
|
|
||||||
|
* Bugfix entities gridrow, grid && improve graph design on homepage. [Lang]
|
||||||
|
|
||||||
|
* Some refactor && prod settings. [Lang]
|
||||||
|
|
||||||
|
* Improve graphics design in game. [Lang]
|
||||||
|
|
||||||
|
* Bugfix grid row in entity. [Lang]
|
||||||
|
|
||||||
|
* Bugfix changePlayer after bomb explosion. [Lang]
|
||||||
|
|
||||||
|
* Improve game graph design. [Lang]
|
||||||
|
|
||||||
|
* Login and register form more design. [Lang]
|
||||||
|
|
||||||
|
* Add basic design to userbundle && refactor. [Lang]
|
||||||
|
|
||||||
|
* Add font-awesome. [Lang]
|
||||||
|
|
||||||
|
* Working user authentication w/ fb and plain login. [Lang]
|
||||||
|
|
||||||
|
* Add facebook login module, hwi/HWIOAuthBundle. [Lang]
|
||||||
|
|
||||||
|
* Login && register form overrided. [Lang]
|
||||||
|
|
||||||
|
* Js and config refactor. [Lang]
|
||||||
|
|
||||||
|
* Replace gridcol object to json array in db. [Lang]
|
||||||
|
|
||||||
|
* Refactor. [Lang]
|
||||||
|
|
||||||
|
* Save steps and point info to db. [Lang]
|
||||||
|
|
||||||
|
* Save the step data to db. [Lang]
|
||||||
|
|
||||||
|
* Renamed the acme to mineseeker && handle when the user connection has been lost. [Lang]
|
||||||
|
|
||||||
|
* Add player names to UI. [Lang]
|
||||||
|
|
||||||
|
* Add overlay && game do not start until the opponent came. [Lang]
|
||||||
|
|
||||||
|
* Add base64 encryption to grid when it has been sended to server. [Lang]
|
||||||
|
|
||||||
|
* On click opponents bomb, you cannot target && refactor. [Lang]
|
||||||
|
|
||||||
|
* Warning when player has been found more than 20 mines. [Lang]
|
||||||
|
|
||||||
|
* Bugfix center mine counter animation. [Lang]
|
||||||
|
|
||||||
|
* The opponent is the next when bomb is exploded. [Lang]
|
||||||
|
|
||||||
|
* Current username checked && refactor && remove players in channel when they are more than 2. [Lang]
|
||||||
|
|
||||||
|
* Send bomb info and use it on opponent. [Lang]
|
||||||
|
|
||||||
|
* Add sounds w/ howler. [Lang]
|
||||||
|
|
||||||
|
* Bugfix multiple empty fields w/ one click on opponent view. [Lang]
|
||||||
|
|
||||||
|
* Refact && remove sound and logging && bugfix BIGBUG - handleGridField and showAppropriateFields sort order... [Lang]
|
||||||
|
|
||||||
|
* Create first working communication. [Lang]
|
||||||
|
|
||||||
|
* Create entities and repositories. [Lang]
|
||||||
|
|
||||||
|
* Changed websocket default port && debug RPC. [Lang]
|
||||||
|
|
||||||
|
* Created working session and client handler w/ websocket. [Lang]
|
||||||
|
|
||||||
|
* Working websocket client and server w/o session handling and storage. [Lang]
|
||||||
|
|
||||||
|
* Composer update. [Lang]
|
||||||
|
|
||||||
|
* Improve game && start sound creating. [Lang]
|
||||||
|
|
||||||
|
* Refactor grid control and grid field. [Lang]
|
||||||
|
|
||||||
|
* Created basic game w/ table and animations. [Lang]
|
||||||
|
|
||||||
|
* Websocket basic setup FE & BE && working basic game w/ react && webpack & babel config. [Lang]
|
||||||
|
|
||||||
|
* Gitignore node_modules && add symlink to node_modules (just for install) && basic react. [Lang]
|
||||||
|
|
||||||
|
* Add react hello world. [Lang]
|
||||||
|
|
||||||
|
* Rename project in config. [Lang]
|
||||||
|
|
||||||
|
* Initial commit && create project in symfony3. [Lang]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
124
LICENSE
Normal file
124
LICENSE
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2026 SplendidBear (https://www.splendidbear.org)
|
||||||
|
|
||||||
|
MineSeeker 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.
|
||||||
|
|
||||||
|
MineSeeker is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS:
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of works.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this License.
|
||||||
|
|
||||||
|
"You" refers to each licensee.
|
||||||
|
|
||||||
|
"Legal entities" means the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
|
||||||
|
"Modify" means to copy from or adapt all or part of the work in a fashion
|
||||||
|
requiring copyright permission, other than the making of an exact copy.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work for making
|
||||||
|
modifications to it. "Object code" means any non-source form of a work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of copyright
|
||||||
|
on the Program. You are granted all permissions necessary to run, modify and
|
||||||
|
propagate covered works by this License.
|
||||||
|
|
||||||
|
3. Copyleft - Derivative Works.
|
||||||
|
|
||||||
|
If you modify the Program, your modified version must:
|
||||||
|
|
||||||
|
- Carry prominent notices stating that you have modified it
|
||||||
|
- License the entire work under this License or a compatible license
|
||||||
|
- Make the source code available to recipients
|
||||||
|
- Preserve all notices of previous licensing
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you receive it,
|
||||||
|
provided that you:
|
||||||
|
|
||||||
|
- Keep intact all notices of authorship and licensing
|
||||||
|
- Give recipients access to the source code along with this License
|
||||||
|
- Do not modify anything except the License itself
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program under this License provided that:
|
||||||
|
|
||||||
|
- The work must be licensed as a whole under this License
|
||||||
|
- You must give prominent notice of any modifications
|
||||||
|
- You must provide access to the Corresponding Source code
|
||||||
|
- You preserve all licensing notices
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
If you convey object code or compiled versions, you must also provide:
|
||||||
|
|
||||||
|
- The Corresponding Source code (in machine-readable form)
|
||||||
|
- A notice of the terms under which it is licensed
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
No additional restrictions may be placed on the exercise of the rights
|
||||||
|
granted or affirmed under this License.
|
||||||
|
|
||||||
|
8. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
9. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING
|
||||||
|
ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF
|
||||||
|
THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS
|
||||||
|
OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
|
||||||
|
THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
For the complete GPL-3.0-or-later license text, visit:
|
||||||
|
https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
For more information about GNU GPL, visit:
|
||||||
|
https://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
MineSeeker is a multiplayer minesweeper game inspired by MSN Messenger's game.
|
||||||
|
Project: https://www.mineseeker.hu
|
||||||
|
Author: SplendidBear (https://www.splendidbear.org)
|
||||||
|
|
||||||
|
|
||||||
4
Makefile
4
Makefile
@@ -11,6 +11,7 @@ help:
|
|||||||
@echo " make down - Stop and remove containers/networks"
|
@echo " make down - Stop and remove containers/networks"
|
||||||
@echo " make prune-everything - Prune volumes, networks and images (DANGEROUS!)"
|
@echo " make prune-everything - Prune volumes, networks and images (DANGEROUS!)"
|
||||||
@echo " make db-reset - Reset the database (drop, create, migrate) (DANGEROUS!)"
|
@echo " make db-reset - Reset the database (drop, create, migrate) (DANGEROUS!)"
|
||||||
|
@echo " make ccp - Clear the production cache"
|
||||||
|
|
||||||
start:
|
start:
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -51,3 +52,6 @@ db-reset:
|
|||||||
bin/console doctrine:database:drop --force --if-exists --no-interaction
|
bin/console doctrine:database:drop --force --if-exists --no-interaction
|
||||||
bin/console doctrine:database:create --if-not-exists --no-interaction
|
bin/console doctrine:database:create --if-not-exists --no-interaction
|
||||||
bin/console doctrine:migrations:migrate --no-interaction
|
bin/console doctrine:migrations:migrate --no-interaction
|
||||||
|
|
||||||
|
ccp:
|
||||||
|
bin/console cache:clear --no-warmup --env=prod
|
||||||
|
|||||||
@@ -7,9 +7,4 @@
|
|||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$font-path: "/build/webfonts";
|
@import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
|
|
||||||
@import '@fortawesome/fontawesome-free/scss/fontawesome';
|
|
||||||
@import '@fortawesome/fontawesome-free/scss/brands';
|
|
||||||
@import '@fortawesome/fontawesome-free/scss/solid';
|
|
||||||
@import '@fortawesome/fontawesome-free/scss/regular';
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
.hero-auth {
|
#hero-auth {
|
||||||
position: absolute;
|
padding: 20px;
|
||||||
top: 28px;
|
|
||||||
right: 36px;
|
.hero-auth {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-auth-user {
|
.hero-auth-user {
|
||||||
font: 600 13px 'Rajdhani', sans-serif;
|
font: 600 13px 'Rajdhani', sans-serif;
|
||||||
color: rgba(149, 207, 245, 0.75);
|
color: rgba(149, 207, 245, 0.75);
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
@@ -17,6 +18,13 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
i { font-size: 15px; }
|
i { font-size: 15px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.hero-auth {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-auth-btn {
|
.hero-auth-btn {
|
||||||
|
|||||||
@@ -33,3 +33,11 @@ main div.txt a {
|
|||||||
|
|
||||||
&:hover { color: #c5e8ff; }
|
&:hover { color: #c5e8ff; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main div.txt img {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main div.txt .img-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ footer {
|
|||||||
gap: 40px;
|
gap: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left: brand block
|
|
||||||
.footer-brand {
|
.footer-brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -55,7 +54,6 @@ footer {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right: navigation
|
|
||||||
.footer-nav-label {
|
.footer-nav-label {
|
||||||
font: 700 11px 'Rajdhani', sans-serif;
|
font: 700 11px 'Rajdhani', sans-serif;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -91,7 +89,6 @@ footer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bottom copyright bar
|
|
||||||
.footer-copy {
|
.footer-copy {
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
padding: 16px 60px;
|
padding: 16px 60px;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mine-container {
|
.mine-container {
|
||||||
background: url("/images/bg-mineseeker-0-outbg.jpg") no-repeat;
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -78,8 +78,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#mine-wrapper .grid .field-wrapper .field .field-corner {
|
#mine-wrapper .grid .field-wrapper .field .field-corner {
|
||||||
background: url('/images/bg-corner-outbg.png') no-repeat top left;
|
|
||||||
background-size: 100%;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,6 @@ footer nav ul li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
footer nav ul li:nth-child(even) {
|
footer nav ul li:nth-child(even) {
|
||||||
width: 50px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +400,4 @@ footer nav ul li a:hover {
|
|||||||
footer nav ul li {
|
footer nav ul li {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer nav ul li:nth-child(even) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ const GridField = memo(function GridField({ cell, onClick, onMouseEnter }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className={fieldClass}>
|
<div className={fieldClass}>
|
||||||
<div className="field-corner">
|
<div
|
||||||
|
style={{ background: "url('/images/bg-corner-outbg.png') no-repeat top left / 100% 100%" }}
|
||||||
|
className="field-corner"
|
||||||
|
>
|
||||||
{isNaN(currentImage) && (
|
{isNaN(currentImage) && (
|
||||||
<div className="flag-mine">
|
<div className="flag-mine">
|
||||||
<img src={currentImage} alt="" />
|
<img src={currentImage} alt="" />
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ services:
|
|||||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||||
MINIO_ENDPOINT: http://minio:9000
|
MINIO_ENDPOINT: http://minio:9000
|
||||||
MINIO_PUBLIC_URL: ${MINIO_PUBLIC_URL:-http://localhost:9000}
|
MINIO_PUBLIC_URL: ${MINIO_PUBLIC_URL:-http://localhost:9000}
|
||||||
TRUSTED_PROXIES: ${TRUSTED_PROXIES}
|
# IMPORTANT: Set TRUSTED_PROXIES to your reverse proxy IP in production.
|
||||||
|
# For Docker on same host, use: 172.17.0.1 (default bridge) or 172.16.0.0/12 (overlay network)
|
||||||
|
# For Kubernetes or external proxy, use the proxy's IP address.
|
||||||
|
# WARNING: Using 0.0.0.0/0 is insecure in production environments!
|
||||||
|
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.1}
|
||||||
volumes:
|
volumes:
|
||||||
- app_var:/app/var
|
- app_var:/app/var
|
||||||
- caddy_data:/data
|
- caddy_data:/data
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ security:
|
|||||||
auth_code_parameter_name: _auth_code
|
auth_code_parameter_name: _auth_code
|
||||||
post_only: true
|
post_only: true
|
||||||
default_target_path: MineSeekerBundle_homepage
|
default_target_path: MineSeekerBundle_homepage
|
||||||
|
always_use_default_target_path: false
|
||||||
prepare_on_login: true
|
prepare_on_login: true
|
||||||
prepare_on_access_denied: true
|
prepare_on_access_denied: true
|
||||||
form_login:
|
form_login:
|
||||||
login_path: MineSeekerBundle_login
|
login_path: MineSeekerBundle_login
|
||||||
check_path: MineSeekerBundle_login
|
check_path: MineSeekerBundle_login
|
||||||
default_target_path: MineSeekerBundle_homepage
|
default_target_path: MineSeekerBundle_homepage
|
||||||
|
always_use_default_target_path: false
|
||||||
username_parameter: _username
|
username_parameter: _username
|
||||||
password_parameter: _password
|
password_parameter: _password
|
||||||
enable_csrf: true
|
enable_csrf: true
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"watch": "vite build --watch",
|
"watch": "vite build --watch",
|
||||||
"build": "vite build && cp -r node_modules/@fortawesome/fontawesome-free/webfonts public/build/webfonts",
|
"build": "vite build",
|
||||||
"lint": "eslint assets/js/"
|
"lint": "eslint assets/js/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/images/privileges/battle.png
Normal file
BIN
public/images/privileges/battle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
public/images/privileges/history.png
Normal file
BIN
public/images/privileges/history.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
public/images/privileges/security.png
Normal file
BIN
public/images/privileges/security.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/privileges/shared-battle.png
Normal file
BIN
public/images/privileges/shared-battle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/privileges/stat.png
Normal file
BIN
public/images/privileges/stat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -111,6 +111,12 @@ class GameController extends AbstractController
|
|||||||
return $this->render('Official/landing.html.twig');
|
return $this->render('Official/landing.html.twig');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/rules', name: 'MineSeekerBundle_rules')]
|
||||||
|
public function rules(): Response
|
||||||
|
{
|
||||||
|
return $this->render('Official/rules.html.twig');
|
||||||
|
}
|
||||||
|
|
||||||
public function sendMail(MailerInterface $mailer, ContactMessage $contactMessage): void
|
public function sendMail(MailerInterface $mailer, ContactMessage $contactMessage): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class MercureController extends AbstractController
|
|||||||
#[Route('/api/game/join/{gameAssoc}', name: 'MineSeekerBundle_api_game_join', methods: ['POST'])]
|
#[Route('/api/game/join/{gameAssoc}', name: 'MineSeekerBundle_api_game_join', methods: ['POST'])]
|
||||||
public function join(string $gameAssoc, Request $request): JsonResponse
|
public function join(string $gameAssoc, Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$this->topicManager->subscribe($gameAssoc, $this->resolveUserName($request), $this->getUser());
|
$this->topicManager->subscribe($gameAssoc, $this->resolveUserName($request), $this->getUser(), $request);
|
||||||
|
|
||||||
return $this->json(['success' => true]);
|
return $this->json(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use App\Form\ResetPasswordFormType;
|
|||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use LogicException;
|
||||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
@@ -64,7 +65,7 @@ class SecurityController extends AbstractController
|
|||||||
#[Route('/logout', name: 'MineSeekerBundle_logout', methods: ['POST'])]
|
#[Route('/logout', name: 'MineSeekerBundle_logout', methods: ['POST'])]
|
||||||
public function logout(): never
|
public function logout(): never
|
||||||
{
|
{
|
||||||
throw new \LogicException('This action is intercepted by the security firewall.');
|
throw new LogicException('This action is intercepted by the security firewall.');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/register', name: 'MineSeekerBundle_register')]
|
#[Route('/register', name: 'MineSeekerBundle_register')]
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, TotpTwo
|
|||||||
#[Column(length: 255, nullable: true)]
|
#[Column(length: 255, nullable: true)]
|
||||||
private ?string $avatarPath = null;
|
private ?string $avatarPath = null;
|
||||||
|
|
||||||
|
#[Column(nullable: true)]
|
||||||
|
private ?bool $consentGiven = null;
|
||||||
|
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
@@ -243,4 +246,15 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, TotpTwo
|
|||||||
$this->backupCodes = $backupCodes;
|
$this->backupCodes = $backupCodes;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isConsentGiven(): ?bool
|
||||||
|
{
|
||||||
|
return $this->consentGiven;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConsentGiven(?bool $consentGiven): self
|
||||||
|
{
|
||||||
|
$this->consentGiven = $consentGiven;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace App\Form;
|
|||||||
|
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||||
@@ -19,6 +20,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
|
|||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Validator\Constraints\Email;
|
use Symfony\Component\Validator\Constraints\Email;
|
||||||
|
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||||
use Symfony\Component\Validator\Constraints\Length;
|
use Symfony\Component\Validator\Constraints\Length;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
@@ -68,6 +70,13 @@ class RegistrationFormType extends AbstractType
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
|
->add('consentGiven', CheckboxType::class, [
|
||||||
|
'label' => 'I have read the Privacy and Data Processing Policy and I consent to the processing of my data.',
|
||||||
|
'mapped' => true,
|
||||||
|
'constraints' => [
|
||||||
|
new IsTrue(message: 'You must agree to the privacy policy to create an account.'),
|
||||||
|
],
|
||||||
|
])
|
||||||
->add('recaptcha', RecaptchaType::class);
|
->add('recaptcha', RecaptchaType::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
namespace App\Interfaces;
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +25,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
|
|||||||
*/
|
*/
|
||||||
interface TopicManagerInterface
|
interface TopicManagerInterface
|
||||||
{
|
{
|
||||||
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user): void;
|
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user, Request $request): void;
|
||||||
|
|
||||||
public function unSubscribe(string $gameAssoc, string $userName): void;
|
public function unSubscribe(string $gameAssoc, string $userName): void;
|
||||||
|
|
||||||
|
|||||||
42
src/Migrations/2026/04/Version20260416094849.php
Normal file
42
src/Migrations/2026/04/Version20260416094849.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* This file is part of the SplendidBear Websites' projects.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 @ www.splendidbear.org
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Migrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Version20260416094849
|
||||||
|
*
|
||||||
|
* @package App\Migrations
|
||||||
|
* @author Lang <https://www.splendidbear.org>
|
||||||
|
* @category Class
|
||||||
|
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||||
|
* @link www.splendidbear.org
|
||||||
|
* @since 2026. 04. 16.
|
||||||
|
*/
|
||||||
|
final class Version20260416094849 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add consent property to user entity for GDPR compliance';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE app_user ADD consent_given BOOLEAN DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE app_user DROP consent_given');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,14 +18,15 @@ use App\Entity\User;
|
|||||||
use App\Interfaces\TopicManagerInterface;
|
use App\Interfaces\TopicManagerInterface;
|
||||||
use App\Repository\PlayedGameRepository;
|
use App\Repository\PlayedGameRepository;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
|
|
||||||
use DateTimeInterface;
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use DateTimeInterface;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Mercure\HubInterface;
|
use Symfony\Component\Mercure\HubInterface;
|
||||||
use Symfony\Component\Mercure\Update;
|
use Symfony\Component\Mercure\Update;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
@@ -52,7 +53,7 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user): void
|
public function subscribe(string $gameAssoc, string $userName, ?UserInterface $user, Request $request): void
|
||||||
{
|
{
|
||||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||||
if (null === $playedGame) {
|
if (null === $playedGame) {
|
||||||
@@ -70,7 +71,7 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
|
|
||||||
/** Save the player to the database on a fresh join */
|
/** Save the player to the database on a fresh join */
|
||||||
if (!$isKnown && $count < 2) {
|
if (!$isKnown && $count < 2) {
|
||||||
$users = $this->saveUserToDb($gameAssoc, $userName, $user, $count + 1);
|
$users = $this->saveUserToDb($gameAssoc, $userName, $user, $count + 1, $request);
|
||||||
$count = $this->getPlayerCount($users);
|
$count = $this->getPlayerCount($users);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,9 +169,6 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
// Normal move
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
$coords = $event['coords'];
|
$coords = $event['coords'];
|
||||||
$player = $event['player']; // 'red' | 'blue'
|
$player = $event['player']; // 'red' | 'blue'
|
||||||
$isBomb = (bool)$event['bomb'];
|
$isBomb = (bool)$event['bomb'];
|
||||||
@@ -243,10 +241,6 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
// Grid helpers
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
|
|
||||||
/** Load the grid rows from the database as a 2-D array. */
|
/** Load the grid rows from the database as a 2-D array. */
|
||||||
private function loadGrid(string $gameAssoc): array
|
private function loadGrid(string $gameAssoc): array
|
||||||
{
|
{
|
||||||
@@ -403,10 +397,6 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
return $mines;
|
return $mines;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
// Database helpers
|
|
||||||
// ------------------------------------------------------------------ //
|
|
||||||
|
|
||||||
private function getPlayedGame(string $gameAssoc): ?PlayedGame
|
private function getPlayedGame(string $gameAssoc): ?PlayedGame
|
||||||
{
|
{
|
||||||
return $this->playedGameRepository->findOneByGameAssoc($gameAssoc);
|
return $this->playedGameRepository->findOneByGameAssoc($gameAssoc);
|
||||||
@@ -462,13 +452,18 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function saveUserToDb(string $gameAssoc, string $userName, ?UserInterface $user, int $count): array
|
private function saveUserToDb(
|
||||||
{
|
string $gameAssoc,
|
||||||
|
string $userName,
|
||||||
|
?UserInterface $user,
|
||||||
|
int $count,
|
||||||
|
Request $request
|
||||||
|
): array {
|
||||||
$playedGame = $this->getPlayedGame($gameAssoc);
|
$playedGame = $this->getPlayedGame($gameAssoc);
|
||||||
|
|
||||||
null !== $user
|
null !== $user
|
||||||
? $this->saveRegisteredUser($userName, $count, $playedGame)
|
? $this->saveRegisteredUser($userName, $count, $playedGame)
|
||||||
: $this->saveAnonUser($userName, $count, $playedGame);
|
: $this->saveAnonUser($userName, $count, $playedGame, $request);
|
||||||
|
|
||||||
$this->entityManager->persist($playedGame);
|
$this->entityManager->persist($playedGame);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
@@ -495,11 +490,14 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function saveAnonUser(string $userName, int $count, PlayedGame $playedGame): void
|
private function saveAnonUser(string $userName, int $count, PlayedGame $playedGame, Request $request): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$anon = new Gamer();
|
$anon = new Gamer();
|
||||||
$anon->setUsername($userName);
|
$anon->setUserName($userName);
|
||||||
|
$anon->setIp($request->getClientIp());
|
||||||
|
$anon->setCountry($this->extractCountry($request));
|
||||||
|
$anon->setUserAgent($request->headers->get('User-Agent'));
|
||||||
$anon->setConnTimestamp(new DateTime());
|
$anon->setConnTimestamp(new DateTime());
|
||||||
$this->entityManager->persist($anon);
|
$this->entityManager->persist($anon);
|
||||||
|
|
||||||
@@ -518,8 +516,8 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
|
|
||||||
private function getUserCollection(PlayedGame $playedGame): array
|
private function getUserCollection(PlayedGame $playedGame): array
|
||||||
{
|
{
|
||||||
$redUser = $playedGame->getRed();
|
$redUser = $playedGame->getRed();
|
||||||
$blueUser = $playedGame->getBlue();
|
$blueUser = $playedGame->getBlue();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'red' => null !== $redUser ? $redUser->getUsername() : '',
|
'red' => null !== $redUser ? $redUser->getUsername() : '',
|
||||||
@@ -527,11 +525,11 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
|
'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '',
|
||||||
'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : '',
|
'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : '',
|
||||||
'redAvatar' => null !== $redUser && null !== $redUser->getAvatarPath()
|
'redAvatar' => null !== $redUser && null !== $redUser->getAvatarPath()
|
||||||
? $this->cacheManager->generateUrl($redUser->getAvatarPath(), 'avatar_thumb')
|
? $this->cacheManager->generateUrl($redUser->getAvatarPath(), 'avatar_thumb')
|
||||||
: null,
|
: null,
|
||||||
'blueAvatar' => null !== $blueUser && null !== $blueUser->getAvatarPath()
|
'blueAvatar' => null !== $blueUser && null !== $blueUser->getAvatarPath()
|
||||||
? $this->cacheManager->generateUrl($blueUser->getAvatarPath(), 'avatar_thumb')
|
? $this->cacheManager->generateUrl($blueUser->getAvatarPath(), 'avatar_thumb')
|
||||||
: null,
|
: null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,4 +583,27 @@ readonly class TopicManager implements TopicManagerInterface
|
|||||||
$this->logger->error('Lobby publish error: ' . $e->getMessage());
|
$this->logger->error('Lobby publish error: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function extractCountry(Request $request): ?string
|
||||||
|
{
|
||||||
|
/** Common headers used by CDNs and proxies to pass country information */
|
||||||
|
$countryHeaders = [
|
||||||
|
'CF-IPCountry', // Cloudflare
|
||||||
|
'CloudFront-Viewer-Country', // AWS CloudFront
|
||||||
|
'X-Country-Code', // Custom header
|
||||||
|
'X-Geoip-Country', // Generic GeoIP header
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($countryHeaders as $header) {
|
||||||
|
$country = $request->headers->get($header);
|
||||||
|
|
||||||
|
if (empty($country)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substr($country, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - The Game{% endblock %}
|
{% block title %} - The Game{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_homepage') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_homepage') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:site_name" content="MineSeeker"/>
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
@@ -23,9 +23,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<section
|
<section id="hero-auth">
|
||||||
class="hero{% if app.request.attributes.get('_route') != 'MineSeekerBundle_homepage' %} hero--compact{% endif %}">
|
|
||||||
|
|
||||||
<div class="hero-auth">
|
<div class="hero-auth">
|
||||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
||||||
<a href="{{ path('MineSeekerBundle_profile') }}" class="hero-auth-btn hero-auth-btn--profile">
|
<a href="{{ path('MineSeekerBundle_profile') }}" class="hero-auth-btn hero-auth-btn--profile">
|
||||||
@@ -56,7 +54,10 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
class="hero{% if app.request.attributes.get('_route') != 'MineSeekerBundle_homepage' %} hero--compact{% endif %}">
|
||||||
<a class="hero-logo" href="{{ path('MineSeekerBundle_homepage') }}">
|
<a class="hero-logo" href="{{ path('MineSeekerBundle_homepage') }}">
|
||||||
<img src="{{ asset('images/mine-logo-txt.png') }}" alt="MineSeeker"/>
|
<img src="{{ asset('images/mine-logo-txt.png') }}" alt="MineSeeker"/>
|
||||||
</a>
|
</a>
|
||||||
@@ -253,6 +254,7 @@
|
|||||||
<p class="footer-nav-label">Navigate</p>
|
<p class="footer-nav-label">Navigate</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ path('MineSeekerBundle_homepage') }}">Homepage</a></li>
|
<li><a href="{{ path('MineSeekerBundle_homepage') }}">Homepage</a></li>
|
||||||
|
<li><a href="{{ path('MineSeekerBundle_rules') }}">Game Rules</a></li>
|
||||||
<li><a href="{{ path('MineSeekerBundle_terms') }}">Terms of Use</a></li>
|
<li><a href="{{ path('MineSeekerBundle_terms') }}">Terms of Use</a></li>
|
||||||
<li><a href="{{ path('MineSeekerBundle_privacy') }}">Privacy Policy</a></li>
|
<li><a href="{{ path('MineSeekerBundle_privacy') }}">Privacy Policy</a></li>
|
||||||
<li><a href="{{ path('MineSeekerBundle_contact') }}">Contact</a></li>
|
<li><a href="{{ path('MineSeekerBundle_contact') }}">Contact</a></li>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<meta property="og:title" content="Your friend challenges YOU!"/>
|
<meta property="og:title" content="Your friend challenges YOU!"/>
|
||||||
<meta property="og:description" content="Do you accept the challenge?"/>
|
<meta property="og:description" content="Do you accept the challenge?"/>
|
||||||
<meta property="og:image"
|
<meta property="og:image"
|
||||||
content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('/images/mine-1600x627.png') }}"/>
|
content="https://{{ app.request.host }}{{ asset('/images/mine-1600x627.png') }}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block stylesheets %}
|
{% block stylesheets %}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Contact{% endblock %}
|
{% block title %} - Contact{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_contact') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_contact') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:site_name" content="MineSeeker"/>
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Privacy Policy{% endblock %}
|
{% block title %} - Privacy Policy{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_privacy') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_privacy') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:site_name" content="MineSeeker"/>
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
|
|||||||
144
templates/Official/rules.html.twig
Normal file
144
templates/Official/rules.html.twig
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
{% extends 'Game/index.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %} - Game Rules{% endblock %}
|
||||||
|
|
||||||
|
{% block metas %}
|
||||||
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
|
<meta property="og:url" content="{{ url('MineSeekerBundle_rules') | replace({'http://': 'https://'}) }}"/>
|
||||||
|
<meta property="og:type" content="website"/>
|
||||||
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
|
<meta property="og:title" content="Game Rules · MineSeeker"/>
|
||||||
|
<meta property="og:description" content="Learn how to play MineSeeker and discover what you unlock by creating a free account."/>
|
||||||
|
<meta property="og:image" content="{{ _ogImage }}"/>
|
||||||
|
<meta property="og:image:width" content="1600"/>
|
||||||
|
<meta property="og:image:height" content="627"/>
|
||||||
|
<meta name="twitter:card" content="summary_large_image"/>
|
||||||
|
<meta name="twitter:title" content="Game Rules · MineSeeker"/>
|
||||||
|
<meta name="twitter:description" content="Learn how to play MineSeeker and discover what you unlock by creating a free account."/>
|
||||||
|
<meta name="twitter:image" content="{{ _ogImage }}"/>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="txt">
|
||||||
|
<h2>MineSeeker Game Rules</h2>
|
||||||
|
|
||||||
|
<p>MineSeeker is a real-time 1v1 twist on the classic minesweeper formula. Two players — <strong>Red</strong> and <strong>Blue</strong> — race over the same hidden minefield, taking turns to <strong>hunt the mines</strong>. Each mine you detonate is claimed in your colour and scores a point. The first player to claim the majority of the mines wins.</p>
|
||||||
|
|
||||||
|
<h3>1. The Board</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>The playing field is a <strong>16×16 grid</strong> of covered cells.</li>
|
||||||
|
<li><strong>51 mines</strong> are hidden randomly across the board at the start of each match.</li>
|
||||||
|
<li>Every non-mine cell displays a number indicating how many of its eight neighbours contain a mine — these numbers are your clues. Cells with no adjacent mines are empty.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>2. Turn Order</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Players alternate turns. On your turn the status bar reads <em>“It is your turn! Make a move”</em>; while you wait it reads <em>“Your buddy is making a move”</em>.</li>
|
||||||
|
<li>On your turn you must perform exactly one action: reveal a cell, flag/unflag a cell, or deploy your bomb.</li>
|
||||||
|
<li><strong>If you hit a mine, you keep your turn</strong> and may click again. Your turn only ends when you reveal a safe cell (or use your bomb).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>3. Revealing Cells</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>Left-click</strong> a covered cell to reveal it.</li>
|
||||||
|
<li>Revealing a <strong>numbered cell</strong> just uncovers the clue — no points are awarded — and your turn ends.</li>
|
||||||
|
<li>Revealing an <strong>empty cell</strong> triggers a <strong>flood-fill</strong> that opens all connected empty cells and their numbered borders in a single move. Flood-fill will never step onto a mine, so empty-area sweeps are always safe.</li>
|
||||||
|
<li><strong>Right-click</strong> a covered cell to place a flag where you suspect a mine. Flagged cells cannot be revealed until unflagged.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>4. Claiming Mines & Scoring</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Clicking a mine is the <strong>goal</strong> of the game, not a failure. The mine is marked with your colour’s flag and scores <strong>one point</strong> for you.</li>
|
||||||
|
<li>You keep the turn and may click again — rack up a streak while you’re hot.</li>
|
||||||
|
<li>Your turn only ends when you finally reveal a safe cell (or deploy your bomb).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>5. The Bomb</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Each player carries <strong>one bomb</strong> per match.</li>
|
||||||
|
<li>Detonating your bomb clears a <strong>5×5 blast radius</strong> (25 cells) around the target. <strong>Every mine inside the radius is claimed for your colour and adds to your score.</strong> Numbered cells in the radius are also revealed.</li>
|
||||||
|
<li>The bomb consumes your turn and can only be used once. Save it for a dense patch of suspected mines to burst ahead on the scoreboard.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>6. Winning the Match</h3>
|
||||||
|
|
||||||
|
<p>A match ends in one of three ways:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Majority reached</strong> — the first player to claim more than half of the mines (26 of 51) wins immediately.</li>
|
||||||
|
<li><strong>A player resigns</strong> — the remaining player wins.</li>
|
||||||
|
<li><strong>Draw</strong> — if neither player reaches the majority and scores end up equal, the match is recorded as a draw.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>7. Playing as a Guest</h3>
|
||||||
|
|
||||||
|
<p>No account is required to play. Just open the game, share the match link with a friend, and play. Guest matches are not saved to a history and carry no stats.</p>
|
||||||
|
|
||||||
|
<h2 style="margin-top: 40px;">Registered User Privileges</h2>
|
||||||
|
|
||||||
|
<p>Creating a free account unlocks everything the guest experience leaves behind. Registration takes under a minute and your email is only used for account recovery.</p>
|
||||||
|
|
||||||
|
<h3>1. Persistent Game History</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Every match you play is recorded with timestamps, the full move list, the final grid, and your opponent’s name.</li>
|
||||||
|
<li>Replay past battles cell-by-cell and share them with a public UUID link so friends can watch your finest detonations.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="img-container">
|
||||||
|
<img style="margin-top: 15px;" src="{{ asset('images/privileges/history.png') }}" alt="Recent Game History" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>2. Player Statistics</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Total games, wins, losses, and draws.</li>
|
||||||
|
<li>Win rate percentage, average score, personal best score, and total mines hit.</li>
|
||||||
|
<li>A 6-month trend dashboard charting wins, losses, and draws per month.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="img-container">
|
||||||
|
<img style="margin-top: 15px;" src="{{ asset('images/privileges/stat.png') }}" alt="Statistics" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>3. Profile & Identity</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Upload a custom <strong>avatar</strong> that appears next to your username on the board and in the shared battles.</li>
|
||||||
|
<li>Your username is reserved — no one else can take it.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>4. Account Security</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>Two-factor authentication (TOTP):</strong> Protect your account with an authenticator app and a set of one-time backup codes.</li>
|
||||||
|
<li><strong>WebAuthn passkeys:</strong> Register one or more hardware/biometric security keys for passwordless sign-in.</li>
|
||||||
|
<li>A dedicated <strong>Security</strong> dashboard to manage backup codes, review registered credentials, and rotate them at any time.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="img-container">
|
||||||
|
<img style="margin-top: 15px;" src="{{ asset('images/privileges/security.png') }}" alt="Security Dashboard" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>5. Shareable Battle Pages</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Each recorded match gets a public page with both players’ names, avatars, final scores, the outcome, and a compact summary of how it played out.</li>
|
||||||
|
<li>Perfect for proving that impossible last-turn comeback.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="img-container">
|
||||||
|
<img style="margin-top: 15px;" src="{{ asset('images/privileges/battle.png') }}" alt="Shareable Battle Pages" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="img-container">
|
||||||
|
<img style="margin-top: 15px;" src="{{ asset('images/privileges/shared-battle.png') }}" alt="Shared Battle Page" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="margin-top: 32px;">Ready to level up? <a href="{{ path('MineSeekerBundle_register') }}">Create your free account</a> or <a href="{{ path('MineSeekerBundle_gamePlay') }}">jump straight into a match</a>.</p>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Terms of Service{% endblock %}
|
{% block title %} - Terms of Service{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_terms') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_terms') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:site_name" content="MineSeeker"/>
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Forgot Password{% endblock %}
|
{% block title %} - Forgot Password{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta name="robots" content="noindex,nofollow"/>
|
<meta name="robots" content="noindex,nofollow"/>
|
||||||
<meta property="og:url" content="{{ app.request.uri }}"/>
|
<meta property="og:url" content="{{ app.request.uri }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Sign In{% endblock %}
|
{% block title %} - Sign In{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta name="robots" content="noindex,nofollow"/>
|
<meta name="robots" content="noindex,nofollow"/>
|
||||||
<meta property="og:url" content="{{ app.request.uri }}"/>
|
<meta property="og:url" content="{{ app.request.uri }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Profile{% endblock %}
|
{% block title %} - Profile{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta name="robots" content="noindex,nofollow"/>
|
<meta name="robots" content="noindex,nofollow"/>
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_profile') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_profile') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="profile"/>
|
<meta property="og:type" content="profile"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Security Settings{% endblock %}
|
{% block title %} - Security Settings{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta name="robots" content="noindex,nofollow"/>
|
<meta name="robots" content="noindex,nofollow"/>
|
||||||
<meta property="og:url" content="{{ url('MineSeekerBundle_profile_security') | replace({'http://': 'https://'}) }}"/>
|
<meta property="og:url" content="{{ url('MineSeekerBundle_profile_security') | replace({'http://': 'https://'}) }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{% block title %} - Register{% endblock %}
|
{% block title %} - Register{% endblock %}
|
||||||
|
|
||||||
{% block metas %}
|
{% block metas %}
|
||||||
{%- set _ogImage = app.request.getSchemeAndHttpHost() ~ asset('images/mine-1600x627.png') -%}
|
{%- set _ogImage = 'https://' ~ app.request.host ~ asset('/images/mine-1600x627.png') -%}
|
||||||
<meta property="og:url" content="{{ app.request.uri }}"/>
|
<meta property="og:url" content="{{ app.request.uri }}"/>
|
||||||
<meta property="og:type" content="website"/>
|
<meta property="og:type" content="website"/>
|
||||||
<meta property="og:site_name" content="MineSeeker"/>
|
<meta property="og:site_name" content="MineSeeker"/>
|
||||||
@@ -117,6 +117,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="auth-field">
|
||||||
|
<label class="auth-checkbox-label" style="display: flex; align-items: flex-start; cursor: pointer; user-select: none;">
|
||||||
|
{{ form_widget(form.consentGiven, {
|
||||||
|
attr: {
|
||||||
|
class: 'auth-checkbox',
|
||||||
|
style: 'margin-right: 10px; margin-top: 3px;'
|
||||||
|
}
|
||||||
|
}) }}
|
||||||
|
<span style="flex: 1; font-size: 14px; line-height: 1.5; color: #666;">
|
||||||
|
I have read the <a href="{{ path('MineSeekerBundle_privacy') }}" target="_blank" style="color: #667eea; text-decoration: none;">Privacy and Data Processing Policy</a> and I consent to the processing of my data. *
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{% if not form.consentGiven.vars.valid %}
|
||||||
|
{% for error in form.consentGiven.vars.errors %}
|
||||||
|
<p class="auth-field-error"><i class="fas fa-circle-exclamation"></i> {{ error.message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="auth-submit">
|
<button type="submit" class="auth-submit">
|
||||||
<i class="fas fa-user-plus"></i> Create Account
|
<i class="fas fa-user-plus"></i> Create Account
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user