初始提交,实现WebSocket聊天室基本功能
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
.idea/
|
||||||
target/
|
target/
|
||||||
pom.xml.tag
|
pom.xml.tag
|
||||||
pom.xml.releaseBackup
|
pom.xml.releaseBackup
|
||||||
|
|||||||
@@ -4,34 +4,17 @@
|
|||||||
你画我猜 SpringBoot Websocket
|
你画我猜 SpringBoot Websocket
|
||||||
|
|
||||||
#### 软件架构
|
#### 软件架构
|
||||||
软件架构说明
|
- 后端:Java21、Maven、SpringBoot
|
||||||
|
- 前端:HTML + jQuery
|
||||||
|
- 通讯:HTTP + Websocket
|
||||||
|
|
||||||
#### 安装教程
|
#### 安装教程
|
||||||
|
|
||||||
1. xxxx
|
1. 修改前端`index.html`中的后端地址
|
||||||
2. xxxx
|
2. java -jar xxx.jar
|
||||||
3. xxxx
|
3. 访问`http://IP:8080`进行游玩
|
||||||
|
|
||||||
#### 使用说明
|
#### 使用说明
|
||||||
|
|
||||||
1. xxxx
|
1. 题库在`resources/topic.json`中,可自行添加。
|
||||||
2. xxxx
|
|
||||||
3. xxxx
|
|
||||||
|
|
||||||
#### 参与贡献
|
|
||||||
|
|
||||||
1. Fork 本仓库
|
|
||||||
2. 新建 Feat_xxx 分支
|
|
||||||
3. 提交代码
|
|
||||||
4. 新建 Pull Request
|
|
||||||
|
|
||||||
|
|
||||||
#### 特技
|
|
||||||
|
|
||||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
|
||||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
|
||||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
|
||||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
|
||||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
|
||||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||||
|
. /usr/local/etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "$(uname)" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||||
|
else
|
||||||
|
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=$(java-config --jre-home)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="$(which javac)"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=$(which readlink)
|
||||||
|
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||||
|
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
||||||
|
fi
|
||||||
|
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||||
|
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
# Remove \r in case we run on Windows within Git Bash
|
||||||
|
# and check out the repository with auto CRLF management
|
||||||
|
# enabled. Otherwise, we may read lines that are delimited with
|
||||||
|
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||||
|
# splitting rules.
|
||||||
|
tr -s '\r\n' ' ' < "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
printf '%s\n' "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||||
|
log "$MAVEN_PROJECTBASEDIR"
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
if [ -r "$wrapperJarPath" ]; then
|
||||||
|
log "Found $wrapperJarPath"
|
||||||
|
else
|
||||||
|
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||||
|
|
||||||
|
if [ -n "$MVNW_REPOURL" ]; then
|
||||||
|
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||||
|
else
|
||||||
|
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||||
|
fi
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||||
|
safeValue=$(echo "$value" | tr -d '\r')
|
||||||
|
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
log "Downloading from: $wrapperUrl"
|
||||||
|
|
||||||
|
if $cygwin; then
|
||||||
|
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
log "Found wget ... using wget"
|
||||||
|
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
log "Found curl ... using curl"
|
||||||
|
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Falling back to using Java to download"
|
||||||
|
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||||
|
# For Cygwin, switch paths to Windows format before running javac
|
||||||
|
if $cygwin; then
|
||||||
|
javaSource=$(cygpath --path --windows "$javaSource")
|
||||||
|
javaClass=$(cygpath --path --windows "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$javaSource" ]; then
|
||||||
|
if [ ! -e "$javaClass" ]; then
|
||||||
|
log " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||||
|
fi
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
log " - Running MavenWrapperDownloader.java ..."
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||||
|
wrapperSha256Sum=""
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||||
|
esac
|
||||||
|
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ -n "$wrapperSha256Sum" ]; then
|
||||||
|
wrapperSha256Result=false
|
||||||
|
if command -v sha256sum > /dev/null; then
|
||||||
|
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||||
|
wrapperSha256Result=true
|
||||||
|
fi
|
||||||
|
elif command -v shasum > /dev/null; then
|
||||||
|
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||||
|
wrapperSha256Result=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||||
|
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $wrapperSha256Result = false ]; then
|
||||||
|
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||||
|
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||||
|
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
# work with both Windows and non-Windows executions.
|
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
||||||
|
export MAVEN_CMD_LINE_ARGS
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086 # safe args
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
$MAVEN_DEBUG_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||||
|
|
||||||
|
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %WRAPPER_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||||
|
SET WRAPPER_SHA_256_SUM=""
|
||||||
|
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||||
|
)
|
||||||
|
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||||
|
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||||
|
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||||
|
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||||
|
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||||
|
" exit 1;"^
|
||||||
|
"}"^
|
||||||
|
"}"
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% ^
|
||||||
|
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||||
|
%MAVEN_OPTS% ^
|
||||||
|
%MAVEN_DEBUG_OPTS% ^
|
||||||
|
-classpath %WRAPPER_JAR% ^
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||||
|
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
cmd /C exit /B %ERROR_CODE%
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.4</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>cn.airnan</groupId>
|
||||||
|
<artifactId>draw-and-guess</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>draw-and-guess</name>
|
||||||
|
<description>draw-and-guess</description>
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>jakarta.websocket</groupId>-->
|
||||||
|
<!-- <artifactId>jakarta.websocket-api</artifactId>-->
|
||||||
|
<!-- <version>2.1.1</version>-->
|
||||||
|
<!-- <scope>provided</scope>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>5.8.26</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>2.0.48</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin><!--编译跳过测试文件检查的生命周期-->
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package cn.airnan.dg;
|
||||||
|
|
||||||
|
import cn.airnan.dg.service.TopicService;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class DrawAndGuessApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DrawAndGuessApplication.class, args);
|
||||||
|
TopicService.loadTopics();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package cn.airnan.dg.configs;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置跨域
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig implements WebMvcConfigurer {
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**").allowedOriginPatterns("*")
|
||||||
|
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
|
.allowCredentials(true).maxAge(3600);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package cn.airnan.dg.configs;
|
||||||
|
|
||||||
|
import cn.airnan.dg.entity.Player;
|
||||||
|
import cn.airnan.dg.entity.Room;
|
||||||
|
import cn.airnan.dg.entity.Topic;
|
||||||
|
import jakarta.websocket.Session;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局缓存变量
|
||||||
|
*/
|
||||||
|
public class Global {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型-房间聊天(回答)
|
||||||
|
*/
|
||||||
|
public static final String MSG_TYPE_ROOM_CHAT = "room-chat";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型-房间绘画
|
||||||
|
*/
|
||||||
|
public static final String MSG_TYPE_ROOM_DRAW = "room-draw";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型-房间通知消息
|
||||||
|
*/
|
||||||
|
public static final String MSG_TYPE_ROOM_MSG = "room-msg";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题库
|
||||||
|
*/
|
||||||
|
public static List<Topic> TOPICS = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间列表
|
||||||
|
*/
|
||||||
|
public static final Map<String, Room> ROOMS = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家列表
|
||||||
|
*/
|
||||||
|
public static final Map<String, Player> PLAYERS = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话列表
|
||||||
|
*/
|
||||||
|
public static final CopyOnWriteArraySet<Session> SESSIONS = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在线连接池
|
||||||
|
*/
|
||||||
|
public static final Map<String, Session> SESSION_POOL = new HashMap<>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package cn.airnan.dg.configs;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebSocketConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter serverEndpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
package cn.airnan.dg.controller;
|
||||||
|
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Player;
|
||||||
|
import cn.airnan.dg.entity.Room;
|
||||||
|
import cn.airnan.dg.service.PlayerService;
|
||||||
|
import cn.airnan.dg.service.RoomService;
|
||||||
|
import cn.airnan.dg.utils.CommonResponse;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家相关接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/player")
|
||||||
|
public class PlayerController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PlayerController.class);
|
||||||
|
@Resource
|
||||||
|
private PlayerService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家列表
|
||||||
|
*
|
||||||
|
* @return 玩家列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public CommonResponse list() {
|
||||||
|
try {
|
||||||
|
return CommonResponse.success(service.getPlayers());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断玩家是否还在
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
@PostMapping("/isExist")
|
||||||
|
public CommonResponse isExist(@RequestBody Player player) {
|
||||||
|
try {
|
||||||
|
return CommonResponse.success(Global.PLAYERS.containsKey(player.getId()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建玩家
|
||||||
|
*
|
||||||
|
* @return 玩家信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/create")
|
||||||
|
public CommonResponse create(@RequestBody Player player) {
|
||||||
|
try {
|
||||||
|
if (service.canCreatePlayer(player.getId())) {
|
||||||
|
var result = service.createPlayer(player);
|
||||||
|
if (result != null) {
|
||||||
|
log.info("【创建玩家】名称:{}", player.getId());
|
||||||
|
return CommonResponse.success(result);
|
||||||
|
} else {
|
||||||
|
return CommonResponse.error("创建玩家失败,请重试!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return CommonResponse.error("玩家名称已存在,换一个吧!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
package cn.airnan.dg.controller;
|
||||||
|
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Player;
|
||||||
|
import cn.airnan.dg.entity.Room;
|
||||||
|
import cn.airnan.dg.service.RoomService;
|
||||||
|
import cn.airnan.dg.utils.CommonResponse;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间相关接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/room")
|
||||||
|
public class RoomController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(RoomController.class);
|
||||||
|
@Resource
|
||||||
|
private RoomService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间列表
|
||||||
|
*
|
||||||
|
* @return 房间列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public CommonResponse list() {
|
||||||
|
try {
|
||||||
|
return CommonResponse.success(service.getRooms());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建房间
|
||||||
|
*
|
||||||
|
* @return 房间信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/create")
|
||||||
|
public CommonResponse create(@RequestBody Room room) {
|
||||||
|
try {
|
||||||
|
if (service.canCreateRoom(room.getId())) {
|
||||||
|
var result = service.createRoom(room);
|
||||||
|
if (result != null) {
|
||||||
|
log.info("【创建房间】{}创建房间:{}", room.getMaster(), room.getId());
|
||||||
|
return CommonResponse.success(result);
|
||||||
|
} else {
|
||||||
|
return CommonResponse.error("创建房间失败,请重试!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return CommonResponse.error("房间名已存在,换一个吧!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以加入房间
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
@PostMapping("/canJoin")
|
||||||
|
public CommonResponse canJoin(@RequestBody Room room) {
|
||||||
|
try {
|
||||||
|
if (service.canJoinRoom(room.getId())) {
|
||||||
|
List<Player> players = Global.ROOMS.get(room.getId()).getPlayers();
|
||||||
|
for (Player player : players) {
|
||||||
|
if (Objects.equals(room.getPlayers().getFirst().getId(), player.getId())) {
|
||||||
|
return CommonResponse.error("已经在房间里了!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CommonResponse.success(true);
|
||||||
|
} else {
|
||||||
|
return CommonResponse.error("房间已满或已经开始游戏,再换一个吧!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
package cn.airnan.dg.controller;
|
||||||
|
|
||||||
|
import cn.airnan.dg.service.TopicService;
|
||||||
|
import cn.airnan.dg.utils.CommonResponse;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目相关接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/topic")
|
||||||
|
public class TopicController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TopicService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机获取题目列表
|
||||||
|
*
|
||||||
|
* @return 题目列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/random")
|
||||||
|
public CommonResponse random() {
|
||||||
|
try {
|
||||||
|
return CommonResponse.success(service.randomTopics(4));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommonResponse.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
package cn.airnan.dg.controller;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web首页
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class indexController {
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String index() {
|
||||||
|
return "index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画板实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Board {
|
||||||
|
/**
|
||||||
|
* 画板ID
|
||||||
|
*/
|
||||||
|
private int id;
|
||||||
|
/**
|
||||||
|
* 画笔点位列表
|
||||||
|
*/
|
||||||
|
private List<Brush> brushes = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加画笔点位
|
||||||
|
*
|
||||||
|
* @param brush 画笔点位
|
||||||
|
*/
|
||||||
|
private void addDraw(Brush brush) {
|
||||||
|
this.brushes.add(brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空画板
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
brushes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画笔实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Brush {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画笔颜色
|
||||||
|
*/
|
||||||
|
private String color;
|
||||||
|
/**
|
||||||
|
* 画笔粗细
|
||||||
|
*/
|
||||||
|
private String width;
|
||||||
|
/**
|
||||||
|
* 画笔坐标点
|
||||||
|
*/
|
||||||
|
private Point point;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import cn.airnan.dg.utils.SocketMsg;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import jakarta.websocket.Session;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Player {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家要绑定的会话
|
||||||
|
*/
|
||||||
|
private Session session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家ID
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家位置
|
||||||
|
*/
|
||||||
|
private Integer index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家分数
|
||||||
|
*/
|
||||||
|
private Integer score = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加分
|
||||||
|
*
|
||||||
|
* @param add 要增加的分数
|
||||||
|
*/
|
||||||
|
private void addScore(Integer add) {
|
||||||
|
this.score += add;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置分数
|
||||||
|
*/
|
||||||
|
private void clearScore() {
|
||||||
|
this.score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给玩家
|
||||||
|
*
|
||||||
|
* @param message 要发送的消息
|
||||||
|
*/
|
||||||
|
public void sendMessage(String type, String message) {
|
||||||
|
Session session = getSession();
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
SocketMsg msg = new SocketMsg();
|
||||||
|
msg.setType(type);
|
||||||
|
msg.setData(message);
|
||||||
|
session.getAsyncRemote().sendText(JSON.toJSONString(msg));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 处理发送消息失败的情况
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 坐标点实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Point {
|
||||||
|
/**
|
||||||
|
* X轴坐标点
|
||||||
|
*/
|
||||||
|
private double x;
|
||||||
|
/**
|
||||||
|
* Y轴坐标点
|
||||||
|
*/
|
||||||
|
private double y;
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Room {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Room.class);
|
||||||
|
/**
|
||||||
|
* 房间ID
|
||||||
|
*/
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房主ID
|
||||||
|
*/
|
||||||
|
private String master;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家列表(最多五人)
|
||||||
|
*/
|
||||||
|
private List<Player> players = new ArrayList<>(5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画板
|
||||||
|
*/
|
||||||
|
private Board board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目
|
||||||
|
*/
|
||||||
|
private Topic topic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 游戏开始状态
|
||||||
|
*/
|
||||||
|
private boolean isBegin = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 倒计时
|
||||||
|
*/
|
||||||
|
private Integer countDown = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加玩家
|
||||||
|
*
|
||||||
|
* @param player 要添加的玩家
|
||||||
|
*/
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
if (players.size() < 5) {
|
||||||
|
players.add(player);
|
||||||
|
} else {
|
||||||
|
System.out.println("房间已满,无法添加更多玩家。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据玩家id移除房间内的玩家
|
||||||
|
*
|
||||||
|
* @param playerId 要移除的玩家id
|
||||||
|
*/
|
||||||
|
public void removePlayerById(String playerId) {
|
||||||
|
Player player = new Player();
|
||||||
|
player.setId(playerId);
|
||||||
|
players.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播消息给房间内所有玩家
|
||||||
|
*
|
||||||
|
* @param type 消息类型
|
||||||
|
* @param message 要广播的消息
|
||||||
|
* @param playerId 发送者的ID(用于排除发送者)
|
||||||
|
*/
|
||||||
|
public void sendMessage(String type, String playerId, String message) {
|
||||||
|
log.info("【房间消息】{}: {}", id, message);
|
||||||
|
for (Player player : players) {
|
||||||
|
// 向除自己之外的玩家发送消息
|
||||||
|
// if (!player.getId().equals(playerId)) {
|
||||||
|
// player.sendMessage(type, message);
|
||||||
|
// }
|
||||||
|
// 向房间内所有玩家发送
|
||||||
|
player.sendMessage(type, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package cn.airnan.dg.entity;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Topic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目提示
|
||||||
|
*/
|
||||||
|
private String tip;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package cn.airnan.dg.pojo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class RoomListVO {
|
||||||
|
private String id;
|
||||||
|
private String master;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package cn.airnan.dg.service;
|
||||||
|
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Player;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 玩家相关业务逻辑
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class PlayerService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取玩家列表
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String, Player> getPlayers() {
|
||||||
|
return Global.PLAYERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建玩家
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Player createPlayer(Player player) {
|
||||||
|
try {
|
||||||
|
Global.PLAYERS.put(player.getId(), player);
|
||||||
|
return player;
|
||||||
|
}catch (Exception e){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以创建玩家
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
public boolean canCreatePlayer(String playerId) {
|
||||||
|
boolean exist = Global.PLAYERS.containsKey(playerId);
|
||||||
|
if (exist) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package cn.airnan.dg.service;
|
||||||
|
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Room;
|
||||||
|
import cn.airnan.dg.pojo.RoomListVO;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 房间相关业务逻辑
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class RoomService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间列表
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<RoomListVO> getRooms() {
|
||||||
|
List<RoomListVO> list = new ArrayList<>();
|
||||||
|
Global.ROOMS.values().forEach(room -> {
|
||||||
|
RoomListVO roomListVO = new RoomListVO();
|
||||||
|
roomListVO.setId(room.getId());
|
||||||
|
roomListVO.setMaster(room.getMaster());
|
||||||
|
list.add(roomListVO);
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建房间
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Room createRoom(Room room) {
|
||||||
|
try {
|
||||||
|
Global.ROOMS.put(room.getId(), room);
|
||||||
|
return room;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以创建房间
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
public boolean canCreateRoom(String roomId) {
|
||||||
|
boolean exist = Global.ROOMS.containsKey(roomId);
|
||||||
|
if (exist) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以加入房间
|
||||||
|
*
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
public boolean canJoinRoom(String roomId) {
|
||||||
|
int result = 5 - Global.ROOMS.get(roomId).getPlayers().size();
|
||||||
|
// 有空位并且游戏没有开始
|
||||||
|
if (result > 0 && !Global.ROOMS.get(roomId).isBegin()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package cn.airnan.dg.service;
|
||||||
|
|
||||||
|
import cn.airnan.dg.DrawAndGuessApplication;
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Topic;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 题目相关业务逻辑
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class TopicService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件加载题库
|
||||||
|
*/
|
||||||
|
public static void loadTopics() {
|
||||||
|
try (InputStream inputStream = DrawAndGuessApplication.class.getResourceAsStream("/topic.json")) {
|
||||||
|
if (inputStream != null) {
|
||||||
|
String content = new String(inputStream.readAllBytes());
|
||||||
|
JSONArray jsonArray = JSON.parseArray(content);
|
||||||
|
for (Object obj : jsonArray) {
|
||||||
|
JSONObject jsonObject = (JSONObject) obj;
|
||||||
|
for (String key : jsonObject.keySet()) {
|
||||||
|
String value = jsonObject.getString(key);
|
||||||
|
Global.TOPICS.add(new Topic(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("共加载{}条题目", Global.TOPICS.size());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机取出N道题目
|
||||||
|
*
|
||||||
|
* @param n 取出的题目数量
|
||||||
|
* @return 题目列表
|
||||||
|
*/
|
||||||
|
public List<Topic> randomTopics(int n) {
|
||||||
|
Map<Integer, String> map = new HashMap();
|
||||||
|
List<Topic> news = new ArrayList();
|
||||||
|
if (Global.TOPICS.size() <= n) {
|
||||||
|
return Global.TOPICS;
|
||||||
|
} else {
|
||||||
|
while (map.size() < n) {
|
||||||
|
int random = (int) (Math.random() * Global.TOPICS.size());
|
||||||
|
if (!map.containsKey(random)) {
|
||||||
|
map.put(random, "");
|
||||||
|
news.add(Global.TOPICS.get(random));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return news;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package cn.airnan.dg.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CommonResponse<T> {
|
||||||
|
private int code;
|
||||||
|
private String msg;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static <T> CommonResponse<T> success(T data) {
|
||||||
|
return new CommonResponse<T>(0, "success", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResponse<T> success() {
|
||||||
|
return new CommonResponse<T>(0, "success", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResponse<T> error(String msg) {
|
||||||
|
return new CommonResponse<T>(1, msg, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package cn.airnan.dg.utils;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket响应体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class SocketMsg<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息类型
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送人
|
||||||
|
*/
|
||||||
|
private String playerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息发送房间
|
||||||
|
*/
|
||||||
|
private String roomId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息内容
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package cn.airnan.dg.utils;
|
||||||
|
|
||||||
|
import cn.airnan.dg.configs.Global;
|
||||||
|
import cn.airnan.dg.entity.Player;
|
||||||
|
import cn.airnan.dg.entity.Room;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.TypeReference;
|
||||||
|
import jakarta.websocket.OnClose;
|
||||||
|
import jakarta.websocket.OnMessage;
|
||||||
|
import jakarta.websocket.OnOpen;
|
||||||
|
import jakarta.websocket.Session;
|
||||||
|
import jakarta.websocket.server.PathParam;
|
||||||
|
import jakarta.websocket.server.ServerEndpoint;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
@ServerEndpoint("/websocket/{playerId}/{roomId}")
|
||||||
|
public class WebSocket {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入房间时触发WebSocket事件
|
||||||
|
*
|
||||||
|
* @param session 当前会话
|
||||||
|
* @param playerId 玩家id
|
||||||
|
* @param roomId 房间id
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session, @PathParam(value = "playerId") String playerId, @PathParam(value = "roomId") String roomId) {
|
||||||
|
try {
|
||||||
|
// 将会话添加到会话列表
|
||||||
|
Global.SESSIONS.add(session);
|
||||||
|
// 将会话添加到连接池
|
||||||
|
Global.SESSION_POOL.put(playerId, session);
|
||||||
|
// 将会话与玩家绑定
|
||||||
|
Player player = Global.PLAYERS.get(playerId);
|
||||||
|
if (player != null) {
|
||||||
|
player.setSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加入房间
|
||||||
|
Room room = Global.ROOMS.get(roomId);
|
||||||
|
if (room != null) {
|
||||||
|
room.addPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知其他玩家有新玩家加入
|
||||||
|
if (room != null) {
|
||||||
|
String message = "玩家【" + playerId + "】加入了房间";
|
||||||
|
room.sendMessage(Global.MSG_TYPE_ROOM_MSG, playerId, message);
|
||||||
|
room.sendMessage(Global.MSG_TYPE_ROOM_CHAT, playerId, "系统:"+message);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("【加入房间】玩家 {} 进入房间 {},房间人数为:{}", playerId, roomId, Global.SESSIONS.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClose
|
||||||
|
public void onClose(Session session) {
|
||||||
|
try {
|
||||||
|
// 从会话列表中移除关闭的会话
|
||||||
|
Global.SESSIONS.remove(session);
|
||||||
|
// 从连接池中移除关闭的会话
|
||||||
|
String playerIdToRemove = null;
|
||||||
|
for (Map.Entry<String, Session> entry : Global.SESSION_POOL.entrySet()) {
|
||||||
|
if (entry.getValue().equals(session)) {
|
||||||
|
playerIdToRemove = entry.getKey();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (playerIdToRemove != null) {
|
||||||
|
Global.SESSION_POOL.remove(playerIdToRemove);
|
||||||
|
}
|
||||||
|
String playerId = "";
|
||||||
|
// 清除玩家与会话的绑定
|
||||||
|
for (Player player : Global.PLAYERS.values()) {
|
||||||
|
if (player.getSession() != null && player.getSession().equals(session)) {
|
||||||
|
player.setSession(null);
|
||||||
|
playerId = player.getId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 从房间中移除玩家
|
||||||
|
for (Room room : Global.ROOMS.values()) {
|
||||||
|
room.removePlayerById(playerId);
|
||||||
|
String message = "玩家【" + playerId + "】退出了游戏,房间人数为:" + room.getPlayers().size();
|
||||||
|
room.sendMessage(Global.MSG_TYPE_ROOM_MSG, playerId, message);
|
||||||
|
room.sendMessage(Global.MSG_TYPE_ROOM_CHAT, playerId, "系统:"+message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message) {
|
||||||
|
// log.info("【WebSocket消息】收到客户端消息:{}", message);
|
||||||
|
try {
|
||||||
|
// 解析消息为 SocketMsg 结构体
|
||||||
|
SocketMsg<String> socketMsg = JSON.parseObject(message, new TypeReference<>() {
|
||||||
|
});
|
||||||
|
// 匹配房间并转发消息
|
||||||
|
Global.ROOMS.get(socketMsg.getRoomId()).sendMessage(socketMsg.getType(), socketMsg.getPlayerId(), socketMsg.getData());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解析消息失败:{}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 此为广播消息
|
||||||
|
*
|
||||||
|
* @param message 消息
|
||||||
|
*/
|
||||||
|
public static void sendAllMessage(String message) {
|
||||||
|
for (Session session : Global.SESSIONS) {
|
||||||
|
try {
|
||||||
|
if (session.isOpen()) {
|
||||||
|
session.getAsyncRemote().sendText(message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 此为单点消息
|
||||||
|
*
|
||||||
|
* @param playerId 用户编号
|
||||||
|
* @param message 消息
|
||||||
|
*/
|
||||||
|
public static void sendOneMessage(String playerId, String message) {
|
||||||
|
Session session = Global.SESSION_POOL.get(playerId);
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
synchronized (session) {
|
||||||
|
session.getAsyncRemote().sendText(message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 此为单点消息(多人)
|
||||||
|
*
|
||||||
|
* @param playerIds 用户编号列表
|
||||||
|
* @param message 消息
|
||||||
|
*/
|
||||||
|
public static void sendMoreMessage(String[] playerIds, String message) {
|
||||||
|
for (String playerId : playerIds) {
|
||||||
|
Session session = Global.SESSION_POOL.get(playerId);
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.getAsyncRemote().sendText(message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getOnLine() {
|
||||||
|
return Global.SESSION_POOL.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: draw-and-guess
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#game-top-box {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#draw-box {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
min-height: 350px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: #000000 solid 1px;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: #daeefd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topic-bar {
|
||||||
|
width: 350px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#topic-bar > div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#board {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: #000000 0 0 1px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-btn {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#player-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 1.4em;
|
||||||
|
color: coral;
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: #00000022 0 0 5px;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-box {
|
||||||
|
width: calc(100vw - 20px);
|
||||||
|
height: calc(100vh - 540px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #00000022;
|
||||||
|
box-shadow: #000000 0 0 1px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.8em;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-input-box {
|
||||||
|
width: 100vw;
|
||||||
|
height: 50px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #bbe1ec;
|
||||||
|
}
|
||||||
|
#chat-input {
|
||||||
|
width: calc(100vw - 100px);
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#home-title {
|
||||||
|
width: 100vw;
|
||||||
|
height: 150px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#home-title label {
|
||||||
|
line-height: 35px;
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#player-id {
|
||||||
|
width: 40vw;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#room-list-box {
|
||||||
|
width: 90vw;
|
||||||
|
height: calc(100vh - 220px);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: #444444 2px solid;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#room-item-title{
|
||||||
|
display: inline-block;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#room-list{
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-item {
|
||||||
|
width: 80vw;
|
||||||
|
height: 60px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 20px;
|
||||||
|
margin: 10px auto;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-item-room, .room-item-player {
|
||||||
|
display: block;
|
||||||
|
height: 30px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-item-room {
|
||||||
|
line-height: 40px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-item-player {
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-item .button {
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
* {
|
||||||
|
outline: 0;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条为黄色,宽度为3px */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条轨道颜色为透明 */
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条滑块颜色为黄色 */
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
max-width: 100vw;
|
||||||
|
max-height: 100vh;
|
||||||
|
min-width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #bbe1ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-box {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 200vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
transition: left 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-home,
|
||||||
|
#page-game {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-home {
|
||||||
|
/* background-color: #faf5b0; */
|
||||||
|
background-color: #bbe1ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-game {
|
||||||
|
background-color: #bbe1ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 8px 10px;
|
||||||
|
line-height: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fdf10d;
|
||||||
|
border: #444444 solid 2px;
|
||||||
|
border-radius: 100px;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
padding: 8px 10px;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: #444444 solid 2px;
|
||||||
|
border-radius: 100px;
|
||||||
|
margin: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input[disabled]{
|
||||||
|
background-color: #e5db63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast{
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: max-content;
|
||||||
|
max-width: 80vw;
|
||||||
|
height: max-content;
|
||||||
|
padding: 10px 20px;
|
||||||
|
top: 100px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #fceb32;
|
||||||
|
border: #000000 solid 2px;
|
||||||
|
color: #000000;
|
||||||
|
border-radius: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,415 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
<title>你画我猜</title>
|
||||||
|
<link rel="stylesheet" href="./css/main.css">
|
||||||
|
<link rel="stylesheet" href="./css/home.css">
|
||||||
|
<link rel="stylesheet" href="./css/game.css">
|
||||||
|
<script src="./js/jquery-3.7.1.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="page-box">
|
||||||
|
<!-- 主页 -->
|
||||||
|
<div id="page-home">
|
||||||
|
<div id="home-title">
|
||||||
|
<h1>你画我猜</h1>
|
||||||
|
<label for="player-id">昵称</label><input type="text" id="player-id" class="input"
|
||||||
|
placeholder="请输入玩家昵称"/>
|
||||||
|
<button class="button" onclick="createPlayer()">创建</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="room-list-box">
|
||||||
|
<span id="room-item-title">房间列表</span>
|
||||||
|
<div id="room-list">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%;display: flex;justify-content: center;">
|
||||||
|
<button class="button" onclick="getRooms()">刷新列表</button>
|
||||||
|
<button class="button" onclick="createRoom()">创建房间</button>
|
||||||
|
</div>
|
||||||
|
<button class="button" onclick="goGame()">切换到游戏页面</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 游戏页面 -->
|
||||||
|
<div id="page-game">
|
||||||
|
<div id="game-top-box">
|
||||||
|
<button id="back-btn" class="button">返回</button>
|
||||||
|
<button id="userIcon" class="button">玩家:admin</button>
|
||||||
|
</div>
|
||||||
|
<div id="draw-box">
|
||||||
|
<div id="topic-bar">
|
||||||
|
<div>题目:<span id="topic-name">坐井观天</span></div>
|
||||||
|
<div>提示:<span id="topic-tip">成语,四个字</span></div>
|
||||||
|
</div>
|
||||||
|
<canvas id="board" width="300" height="300"></canvas>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="black-btn" class="color-btn" style="background-color: #000000;"></button>
|
||||||
|
<button id="red-btn" class="color-btn" style="background-color: #ff0000;"></button>
|
||||||
|
<button id="green-btn" class="color-btn" style="background-color: #00ff00;"></button>
|
||||||
|
<button id="blue-btn" class="color-btn" style="background-color: #0000ff;"></button>
|
||||||
|
<button id="yellow-btn" class="color-btn" style="background-color: #ffff00;"></button>
|
||||||
|
</div>
|
||||||
|
<div><label for="widthPicker">选择画笔宽度:</label>
|
||||||
|
<input type="range" id="widthPicker" value="1" min="1">
|
||||||
|
</div>
|
||||||
|
<div id="player-box">
|
||||||
|
<div class="player-item">张</div>
|
||||||
|
<div class="player-item">王</div>
|
||||||
|
<div class="player-item"></div>
|
||||||
|
<div class="player-item"></div>
|
||||||
|
<div class="player-item"></div>
|
||||||
|
</div>
|
||||||
|
<div id="chat-box">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="chat-input-box">
|
||||||
|
<input id="chat-input" type="text" autocomplete="off" placeholder="输入文字以发送" class="input"/>
|
||||||
|
<button class="button" onclick="sendInput()">发送</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// WebSocket实例
|
||||||
|
let ws = null;
|
||||||
|
// 当前玩家id
|
||||||
|
let playerId = "";
|
||||||
|
// 当前房间id
|
||||||
|
let roomId = "";
|
||||||
|
let backendAddr = "192.168.31.222:8080"
|
||||||
|
const api = "http://" + backendAddr + "/api"
|
||||||
|
init();
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
window.addEventListener("beforeunload", (event) => {
|
||||||
|
// Cancel the event as stated by the standard.
|
||||||
|
event.preventDefault();
|
||||||
|
// Chrome requires returnValue to be set.
|
||||||
|
event.returnValue = "";
|
||||||
|
});
|
||||||
|
isHasWS();
|
||||||
|
getLocalPlayer();
|
||||||
|
getRooms();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断WebSocket是否可用
|
||||||
|
*/
|
||||||
|
function isHasWS() {
|
||||||
|
if ("WebSocket" in window) {
|
||||||
|
console.log("WebSocket可用");
|
||||||
|
} else {
|
||||||
|
alert("浏览器不支持WebSocket,无法进行游戏,请尝试其他浏览器");
|
||||||
|
$("body").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地玩家信息
|
||||||
|
*/
|
||||||
|
function getLocalPlayer() {
|
||||||
|
playerId = window.localStorage.getItem("playerId");
|
||||||
|
if (playerId === undefined || playerId === null || playerId === "") {
|
||||||
|
playerId = "";
|
||||||
|
} else {
|
||||||
|
// 本地存在时判断服务端是否还在
|
||||||
|
$.ajax({
|
||||||
|
url: api + "/player/isExist",
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({id: playerId}),
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code === 0) {
|
||||||
|
if (data.data === true) {
|
||||||
|
$("#player-id").val(playerId);
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem("playerId");
|
||||||
|
showMsg("服务端玩家已注销,请重新创建!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showMsg(data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
// 请求失败时的处理逻辑
|
||||||
|
console.error('请求失败:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建玩家
|
||||||
|
*/
|
||||||
|
function createPlayer() {
|
||||||
|
let postData = {
|
||||||
|
id: $("#player-id").val().trim()
|
||||||
|
}
|
||||||
|
if (postData.id == null || postData.id == undefined || postData.id == "") {
|
||||||
|
return showMsg("玩家昵称不能为空!")
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: api + "/player/create",
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify(postData),
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showMsg("创建成功!");
|
||||||
|
window.localStorage.setItem("playerId", postData.id);
|
||||||
|
playerId = postData.id;
|
||||||
|
} else {
|
||||||
|
showMsg(data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
// 请求失败时的处理逻辑
|
||||||
|
console.error('请求失败:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取房间列表
|
||||||
|
*/
|
||||||
|
function getRooms() {
|
||||||
|
$("#room-list").html("");
|
||||||
|
$.ajax({
|
||||||
|
url: api + "/room/list",
|
||||||
|
type: 'GET',
|
||||||
|
success: function (data) {
|
||||||
|
// 成功接收到响应后的处理逻辑
|
||||||
|
showMsg("房间列表已刷新");
|
||||||
|
if (data.code === 0) {
|
||||||
|
let roomsData = data.data;
|
||||||
|
if (!Array.isArray(roomsData) || roomsData.length === 0) {
|
||||||
|
$("#room-list").html("—— 暂无房间 ——")
|
||||||
|
}
|
||||||
|
$.each(roomsData, function (index, room) {
|
||||||
|
// 创建房间元素
|
||||||
|
var $roomItem = $('<div class="room-item"></div>');
|
||||||
|
var $roomInfo = $('<div></div>');
|
||||||
|
var $roomName = $('<span class="room-item-room">' + room.id + '</span>');
|
||||||
|
var $playerInfo = $('<span class="room-item-player">@' + room.master + '</span>');
|
||||||
|
var $joinButton = $('<button class="button">加入房间</button>');
|
||||||
|
|
||||||
|
// 将房间名称和玩家信息添加到房间信息元素中
|
||||||
|
$roomInfo.append($roomName);
|
||||||
|
$roomInfo.append($playerInfo);
|
||||||
|
|
||||||
|
// 将房间信息和加入按钮添加到房间元素中
|
||||||
|
$roomItem.append($roomInfo);
|
||||||
|
$roomItem.append($joinButton);
|
||||||
|
|
||||||
|
// 将房间元素添加到页面中
|
||||||
|
$("#room-list").append($roomItem);
|
||||||
|
|
||||||
|
// 绑定按钮点击事件
|
||||||
|
$joinButton.on('click', function () {
|
||||||
|
if (playerId === "") {
|
||||||
|
return showMsg("请先创建玩家!");
|
||||||
|
}
|
||||||
|
var result = confirm("确定要加入房间【" + room.id + "】吗?");
|
||||||
|
if (result) {
|
||||||
|
joinRoom(room.id);
|
||||||
|
} else {
|
||||||
|
showMsg("取消操作")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
// 请求失败时的处理逻辑
|
||||||
|
console.error('请求失败:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRoom() {
|
||||||
|
if (playerId === "") {
|
||||||
|
return showMsg("请先创建玩家!");
|
||||||
|
}
|
||||||
|
let createInput = window.prompt("请输入房间名称:", "");
|
||||||
|
if (createInput !== null) {
|
||||||
|
if (createInput.trim() === "") {
|
||||||
|
return showMsg("房间名不能为空!")
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: api + "/room/create",
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({id: createInput, master: playerId}),
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code === 0) {
|
||||||
|
showMsg("创建成功!");
|
||||||
|
var result = confirm("创建成功,是否现在进入房间【" + createInput + "】?");
|
||||||
|
if (result) {
|
||||||
|
joinRoom(createInput)
|
||||||
|
}
|
||||||
|
getRooms();
|
||||||
|
} else {
|
||||||
|
showMsg(data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
// 请求失败时的处理逻辑
|
||||||
|
console.error('请求失败:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMsg(message) {
|
||||||
|
var $toast = $('<div class="toast"></div>').text(message).hide();
|
||||||
|
$('body').append($toast);
|
||||||
|
$toast.fadeIn(100);
|
||||||
|
setTimeout(function () {
|
||||||
|
$toast.fadeOut(100);
|
||||||
|
setTimeout(function () {
|
||||||
|
$toast.remove();
|
||||||
|
}, 600);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinRoom(rid) {
|
||||||
|
// 判断是否可以加入
|
||||||
|
$.ajax({
|
||||||
|
url: api + "/room/canJoin",
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({id: rid, players: [{id: playerId}]}),
|
||||||
|
success: function (data) {
|
||||||
|
if (data.code === 0) {
|
||||||
|
let url = "ws://" + backendAddr + "/websocket/" + playerId + "/" + rid;
|
||||||
|
// 打开一个 web socket
|
||||||
|
ws = new WebSocket(url);
|
||||||
|
ws.onopen = function () {
|
||||||
|
roomId = rid;
|
||||||
|
// 发送握手消息
|
||||||
|
sendMessage("room-chat", playerId + ":大家好,我是" + playerId);
|
||||||
|
goGame();
|
||||||
|
};
|
||||||
|
ws.onmessage = function (evt) {
|
||||||
|
let result = JSON.parse(evt.data);
|
||||||
|
//接收到通知消息
|
||||||
|
if (result.type === "room-msg") {
|
||||||
|
showMsg(result.data);
|
||||||
|
}
|
||||||
|
if (result.type === "room-chat") {
|
||||||
|
$("#chat-box").append("<p>" + result.data + "</p>")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onclose = function () {
|
||||||
|
// 关闭 websocket
|
||||||
|
ws = null;
|
||||||
|
roomId = "";
|
||||||
|
};
|
||||||
|
ws.onerror = function (err) {
|
||||||
|
showMsg("加入失败,请重试");
|
||||||
|
roomId = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showMsg(data.msg)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
// 请求失败时的处理逻辑
|
||||||
|
console.error('请求失败:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止WebSocket
|
||||||
|
*/
|
||||||
|
function StopWS() {
|
||||||
|
if (ws !== null) {
|
||||||
|
ws.close();
|
||||||
|
ws = null;
|
||||||
|
roomId = "";
|
||||||
|
}
|
||||||
|
document.getElementById("image").src = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向服务端发送消息
|
||||||
|
*/
|
||||||
|
function sendMessage(type, data) {
|
||||||
|
if (getWsState() !== 1) {
|
||||||
|
return showMsg("WebSocket未连接,无法发送消息!");
|
||||||
|
}
|
||||||
|
let msg = {
|
||||||
|
type,
|
||||||
|
playerId,
|
||||||
|
roomId,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取WebSocket状态
|
||||||
|
* 0 (WebSocket.CONNECTING)正在链接中
|
||||||
|
* 1 (WebSocket.OPEN)已经链接并且可以通讯
|
||||||
|
* 2 (WebSocket.CLOSING)连接正在关闭
|
||||||
|
* 3 (WebSocket.CLOSED)连接已关闭或者没有链接成功
|
||||||
|
*/
|
||||||
|
function getWsState() {
|
||||||
|
if (ws === null) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
return ws.readyState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendInput() {
|
||||||
|
let txt = $("#chat-input").val().trim();
|
||||||
|
if (txt !== "") {
|
||||||
|
sendMessage("room-chat", playerId + ":" + txt);
|
||||||
|
$("#chat-input").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听键盘事件
|
||||||
|
document.getElementById('chat-input').addEventListener('keydown', function (event) {
|
||||||
|
// 判断是否按下回车键(键码为13)
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
// 处理回车键事件
|
||||||
|
sendInput();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听输入框的键盘事件
|
||||||
|
document.getElementById('chat-input').addEventListener('input', function (event) {
|
||||||
|
// 判断是否按下回车键
|
||||||
|
if (event.inputType === 'insertLineBreak') {
|
||||||
|
// 处理回车键事件
|
||||||
|
sendInput();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function goGame() {
|
||||||
|
$("#page-box").css("left", "-100vw");
|
||||||
|
$("#userIcon").html("玩家:" + playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function goHome() {
|
||||||
|
$("#page-box").css("left", "0px");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
|
|||||||
|
[
|
||||||
|
{"落井下石":"成语、4个字"},
|
||||||
|
{"画饼充饥":"成语、4个字"},
|
||||||
|
{"虎头蛇尾":"成语、4个字"},
|
||||||
|
{"泪流满面":"成语、4个字"},
|
||||||
|
{"捧腹大笑":"成语、4个字"},
|
||||||
|
{"画蛇添足":"成语、4个字"},
|
||||||
|
{"一手遮天":"成语、4个字"},
|
||||||
|
{"羊入虎口":"成语、4个字"},
|
||||||
|
{"掩耳盗铃":"成语、4个字"},
|
||||||
|
{"布娃娃":"儿童玩具、3个字"},
|
||||||
|
{"亡羊补牢":"成语、4个字"},
|
||||||
|
{"颠三倒四":"成语、4个字"},
|
||||||
|
{"拔苗助长":"成语、4个字"},
|
||||||
|
{"顺手牵羊":"成语、4个字"},
|
||||||
|
{"三长二短":"成语、4个字"},
|
||||||
|
{"抱头鼠窜":"成语、4个字"},
|
||||||
|
{"鸡鸣狗盗":"成语、4个字"},
|
||||||
|
{"头破血流":"成语、4个字"},
|
||||||
|
{"坐井观天":"成语、4个字"},
|
||||||
|
{"眼高手低":"成语、4个字"},
|
||||||
|
{"目瞪口呆":"成语、4个字"},
|
||||||
|
{"牛头马面":"成语、4个字"},
|
||||||
|
{"哈哈大笑":"成语、4个字"},
|
||||||
|
{"丢三落四":"成语、4个字"},
|
||||||
|
{"三心二意":"成语、4个字"},
|
||||||
|
{"一刀两断":"成语、4个字"},
|
||||||
|
{"空调":"日常物品、2个字"},
|
||||||
|
{"面粉":"日常物品、2个字"},
|
||||||
|
{"口红":"日常物品、2个字"},
|
||||||
|
{"铅笔":"日常物品、2个字"},
|
||||||
|
{"足球":"日常物品、2个字"},
|
||||||
|
{"酱油":"日常物品、2个字"},
|
||||||
|
{"大米":"日常物品、2个字"},
|
||||||
|
{"香水":"日常物品、2个字"},
|
||||||
|
{"橡皮":"日常物品、2个字"},
|
||||||
|
{"篮球":"日常物品、2个字"},
|
||||||
|
{"牛奶":"日常物品、2个字"},
|
||||||
|
{"馒头":"日常物品、2个字"},
|
||||||
|
{"雨伞":"日常物品、2个字"},
|
||||||
|
{"书包":"日常物品、2个字"},
|
||||||
|
{"冰箱":"日常物品、2个字"},
|
||||||
|
{"眼镜":"日常物品、2个字"},
|
||||||
|
{"啤酒":"日常物品、2个字"},
|
||||||
|
{"面条":"日常物品、2个字"},
|
||||||
|
{"超市":"日常物品、2个字"},
|
||||||
|
{"象棋":"日常物品、2个字"},
|
||||||
|
{"蛋糕":"日常物品、2个字"},
|
||||||
|
{"电话":"日常物品、2个字"},
|
||||||
|
{"手机":"日常物品、2个字"},
|
||||||
|
{"剪刀":"日常物品、2个字"},
|
||||||
|
{"凳子":"日常物品、2个字"},
|
||||||
|
{"火锅":"日常物品、2个字"},
|
||||||
|
{"茶叶":"日常物品、2个字"},
|
||||||
|
{"勺子":"日常物品、2个字"},
|
||||||
|
{"筷子":"日常物品、2个字"},
|
||||||
|
{"杯子":"日常物品、2个字"},
|
||||||
|
{"雨鞋":"日常物品、2个字"},
|
||||||
|
{"袜子":"日常物品、2个字"},
|
||||||
|
{"毛巾":"日常物品、2个字"},
|
||||||
|
{"脸盆":"日常物品、2个字"},
|
||||||
|
{"电脑":"日常物品、2个字"}
|
||||||
|
]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package cn.airnan.dg;
|
||||||
|
|
||||||
|
import cn.airnan.dg.service.TopicService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class DrawAndGuessApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
TopicService.loadTopics();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user