Merge branch 'general-devel' of https://github.com/Pryaxis/TShock

 Conflicts:
	CHANGELOG.md
	TShockAPI/GetDataHandlers.cs
	TShockAPI/TShock.cs
This commit is contained in:
鱼鱼 2020-05-17 21:38:07 +08:00
commit 43f586cde5
93 changed files with 8304 additions and 5851 deletions

57
.all-contributorsrc Normal file
View file

@ -0,0 +1,57 @@
{
"files": [
"README.md",
"README_cn.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "AviKav",
"name": "AviKav",
"avatar_url": "https://avatars2.githubusercontent.com/u/18518861?v=4",
"profile": "https://avikav.net",
"contributions": [
"bug",
"test"
]
},
{
"login": "AxisKriel",
"name": "Rodrigo Rente",
"avatar_url": "https://avatars0.githubusercontent.com/u/3332657?v=4",
"profile": "https://tshock.co",
"contributions": [
"code",
"projectManagement",
"test"
]
},
{
"login": "sgkoishi",
"name": "Stargazing Koishi",
"avatar_url": "https://avatars2.githubusercontent.com/u/9637711?v=4",
"profile": "https://sgkoi.dev",
"contributions": [
"code",
"infra"
]
},
{
"login": "AxeelAnder",
"name": "Axeel",
"avatar_url": "https://avatars2.githubusercontent.com/u/25691207?v=4",
"profile": "https://github.com/AxeelAnder",
"contributions": [
"doc",
"projectManagement"
]
}
],
"contributorsPerLine": 7,
"projectName": "TShock",
"projectOwner": "Pryaxis",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true
}

3
.github/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,3 @@
> By participating in the TShock for Terraria community, all members will adhere to maintaining decorum with respect to all humans, in and out of the community. Members will not engage in discussion that inappropriately disparages or marginalizes any group of people or any individual. Members will not attempt to further or advance an agenda to the point of being overbearing or close minded (such as through spreading FUD). Members will not abuse services provided to them and will follow the guidance of community leaders on a situational basis about what abuse consists of. Members will adhere to United States and international law. If members notice a violation of this code of conduct, they will not engage but will instead contact the leadership team on either the forums or Discord.
> Do not attempt to circumvent or bypass the code of conduct by using clever logic or reasoning (e.g., insulting Facepunch members, because they weren't directly mentioned here).

View file

@ -8,38 +8,26 @@ Please follow these simple requirements before posting a bug report:
- How to reproduce the issue
- Screenshots of the issue (if applicable)
### To build the source
Note: This includes the API by default. If you need only the API, you need to cd into that folder and do the following with the .sln file for the API. For those new to C#, the .sln and .csproj files contain the necessary definitions to do a complete source build using Microsoft or Mono build tools.
- Checkout the source.
- Initialize the submodules: ```git submodule update --init```
- Open the source in your favorite text editor that supports .NET building and press the build button OR
- Run ```msbuild TShock.sln``` in the root of the cloned folder on Windows in a 'Developer Command Prompt' OR
- Run ```xbuild TShock.sln``` in the root of the cloned folder on Unix.
Need help? Drop by Slack and we'll be happy to explain it with more words, step by step.
### TShock Additions
If something is better suited to be a plugin for TShock, rather than a TShock core feature, it should not be added! Project scope is at times questionable, though, so create an issue on Github for discussion first. If an issue is completely outside of the scope of TShock, it will be made clear in that issue what it is.
_If you are confused, make a suggestion. We will determine scope and relevance for you._
_If a person makes a suggestion in Slack, capture the suggestion as a Github issue. If a suggestion crops up on the forums, make a Github issue to capture it. If you want, direct the user to make a suggestion on Github, but set an alarm/timer/reminder so that if they don't know how to use Github or they don't have an account, an issue is still made and discussed. Make it clear that the issue is a surrogate issue for a suggestion from Slack/the forums too._
_If a person makes a suggestion in Discord, capture the suggestion as a Github issue. If a suggestion crops up on the forums, make a Github issue to capture it. If you want, direct the user to make a suggestion on Github, but set an alarm/timer/reminder so that if they don't know how to use Github or they don't have an account, an issue is still made and discussed. Make it clear that the issue is a surrogate issue for a suggestion from Discord/the forums too._
### Pull Request Dev Guidelines
These guidelines are for all contributors.
* Create an issue first to suggest an improvement or feature addition to TShock.
* Active developers will then give a go/no go for implementation. This is scope related: if an issue is within the scope of TShock, it will be tagged 'pr-wanted.'
* After 'pr-wanted' has been added, an issue should be considered workable in a pull request fashion.
* If you, as a developer, want to claim an issue for a PR, as soon as possible start work and note that in both the original issue and the new PR. The 'pr-wanted' tag will remain but the active PR will become the center for discussion for your implementation.
* Active developers will then give a go/no go for implementation. This is scope related: if an issue is within the scope of TShock, it will be tagged 'Contribution Wanted.'
* After 'Contribution Wanted' has been added, an issue should be considered workable in a pull request fashion.
* If you, as a developer, want to claim an issue for a PR, as soon as possible start work and note that in both the original issue and the new PR. The 'Contribution Wanted' tag will remain but the active PR will become the center for discussion for your implementation.
* If a TShock core developer takes an issue, they'll be assigned to the issue. If your issue was taken by a TShock developer and you were actively developing it in a PR, you should _make it clear as soon as possible that a process error has been made_ so that the your development resources and our development resources aren't wasted.
* Please send a pull request with at least a sentence description and something meaningful as the title, not just the issue number you're fixing.
_The pr-wanted tag indicates an issue should be implemented. If an issue has a developer assigned, it indicates that they're working on it. When in doubt, ask where an issue is before starting work (so you don't waste time)!_
_The tag indicates an issue should be implemented. If an issue has a developer assigned, it indicates that they're working on it. When in doubt, ask where an issue is before starting work (so you don't waste time)!_
Even if you have write access to the repository, follow [Github flow](https://guides.github.com/introduction/flow/) when sending commits. Don't send commits directly to either ```master``` or ```general-devel``` unless those commits modify either the deploy scripts or non-code components. If it compiles, follow Github Flow.
@ -56,6 +44,7 @@ Required:
- When using static methods on primitives, use the CLR type. E.g. ```String.Format``` instead of ```string.Format```.
- Always use properties, not public fields.
- Document deprecations and fail compilation if they're included with ```[Obsolete("Use blah instead of blahx...", true)]```.
- Update the `CHANGELOG.md` file.
### Dev Team Guidelines

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
custom: https://www.givedirectly.org/

23
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,23 @@
!!!!!! PLEASE FILL IN THE TEMPLATE BELOW THANK YOU VERY MUCH !!!!!!
If you don't need help, delete this template and just post an issue (feature requests and discussions and the like).
* TShock version:
* TShock build number (if known):
#### Reproduction steps (if applicable)?
1. Some step
2. Some other step
3. Some bigger step
4. This is the problem
#### Any stack traces or error messages (if known)?
```
PUT SUPER LONG ERROR MESSAGES IN THE TICK MARKS
```
#### Any screenshots?
!!!!!! PLEASE FILL IN THE TEMPLATE ABOVE THANK YOU VERY MUCH !!!!!!

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Get help with TShock
url: https://github.com/Pryaxis/TShock/discussions/new
about: If you aren't sure if you have a bug, or if you're having any problems with TShock, post a question here.
- name: Chat with people on Discord
url: https://discord.gg/Cav9nYX
about: If you wanna chill or brainstorm, here's the place to do that.
- name: Don't talk about TShock
url: https://t.me/refugeecamp
about: Where the TShock developers chat about nothing related to TShock.
- name: 在QQ群提问
url: https://jq.qq.com/?_wv=1027&k=5GJZCe4
about: 如果你不会英语或想使用中文进行交流可以加入我们的官方QQ群

32
.github/ISSUE_TEMPLATE/defect-report.md vendored Normal file
View file

@ -0,0 +1,32 @@
---
name: Defect report
about: Report a software defect in TShock (a problem you know is our fault)
title: ''
labels: ''
assignees: ''
---
<!-- Please provide the information requested below -->
* TShock version:
* TShock build number (if known):
#### Reproduction steps (if applicable)?
1. Some step
2. Some other step
3. Some bigger step
4. This is the problem
#### Any stack traces or error messages (if known)?
```
PUT SUPER LONG ERROR MESSAGES IN THE TICK MARKS
```
#### Any screenshots?
#### Any log messages from files that end in `.log`?
#### What plugins and what versions of those plugins are you running?

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest a change to be made to TShock
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

19
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,19 @@
<!-- Warning: If you create a pull request and wish to remain anonymous, you are highly advised to use Tails (https://tails.boum.org/) or a fresh git environment. We will *not* be able to help with anonymization after your pull request has been created. -->
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????
?????? HAVE YOU UPDATED THE CHANGELOG? ??????

10
.github/config.yml vendored Normal file
View file

@ -0,0 +1,10 @@
updateDocsComment: >
Thanks for the pull request! TShock's maintainers would like you to go ahead and give yourself credit by updating the `CHANGELOG.md` as soon as you can. Your pull request will likely not be accepted without this. This both helps us document changes to TShock, as well as give you credit for your work. You deserve it, so go take credit! :sparkles:
updateDocsWhiteList:
- bug
- chore
updateDocsTargetFiles:
- README
- CHANGELOG.md

13
.github/no-response.yml vendored Normal file
View file

@ -0,0 +1,13 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 7
# Label requiring a response
responseRequiredLabel: followup-required
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.

63
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Build Server
on: [push]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: Install nuget
run: choco install nuget.commandline
- name: OTAPI Debug
shell: cmd
run: |
nuget restore .\TerrariaServerAPI\TShock.4.OTAPI.sln
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TShock.4.OTAPI.sln /p:Configuration=Debug
cd .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Debug
TShock.Modifications.Bootstrapper.exe
- name: OTAPI Release
shell: cmd
run: |
nuget restore .\TerrariaServerAPI\TShock.4.OTAPI.sln
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TShock.4.OTAPI.sln /p:Configuration=Release
cd .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Release
TShock.Modifications.Bootstrapper.exe
- name: TerrariaServerAPI Debug
shell: cmd
run: |
cd .\TerrariaServerAPI
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TerrariaServerAPI.csproj /p:Configuration=Debug
- name: TShock Debug
shell: cmd
run: |
nuget restore TShock.sln
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TShockAPI\TShockAPI.csproj /p:Configuration=Debug
- name: TerrariaServerAPI Release
shell: cmd
run: |
cd .\TerrariaServerAPI
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TerrariaServerAPI\TerrariaServerAPI.csproj /p:Configuration=Release
- name: TShock Release
shell: cmd
run: |
nuget restore TShock.sln
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" .\TShockAPI\TShockAPI.csproj /p:Configuration=Release
- uses: actions/upload-artifact@master
with:
name: Experimental TShock (not debug)
path: TShockAPI\bin\Release
- uses: actions/upload-artifact@master
with:
name: Experimental TShock (debug)
path: TShockAPI\bin\Debug
- uses: actions/upload-artifact@master
with:
name: Experimental (debug) OTAPI Bootstrapper
path: .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Debug\TShock.Modifications.Bootstrapper.exe
- uses: actions/upload-artifact@master
with:
name: Experimental (not debug) OTAPI Bootstrapper
path: .\TerrariaServerAPI\TShock.Modifications.Bootstrapper\bin\Release\TShock.Modifications.Bootstrapper.exe

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ Thumbs.db
*.csproj.user
*/_ReSharper*/*
*.user
.vs/*
#Template Bat file#
###################

View file

@ -1,11 +0,0 @@
language: csharp
solution: "./TShockAPI/TShockAPI.csproj"
sudo: false
install:
- nuget restore
script: python ./scripts/create_release.py
notifications:
slack:
secure: O4Nibe2fdaUa2ZxuETUg6WEoQKvNM2CotnfaIVgm3fjfe61dfE1P+EgTpbwDG8646jSmpTqMDw8Z6I/WJwGTlXV/ZQsbwu63Cps4MgOTvPHZ0Lsye5azySlJZs1iI4ItYSj2czXfcnJ+qAl1SOOkXJrjB5uyTMWtDpCrSCFB3MA=
webhooks:
secure: dbTvcMtts5hSgV3DvlHPh36LTOvSPzQbVRUrgN9j0M/MlCm1QlBVt1vDLzN8VbkSYXiJYVWGMDpSHApL6SBu7sEQaXeC4zZyTMX76PeKw5a5xh0mIdDyg8Ls9WVA+QDVGes5DA1CZWbVRBDto3U0c+Ob8iza3o01sEFWpm7wQg4=

277
README.md
View file

@ -1,46 +1,269 @@
<p align="center">
<img src="https://tshock.co/newlogo.png" alt="TShock for Terraria"><br />
<a href="https://travis-ci.org/NyxStudios/TShock"><img src="https://travis-ci.org/NyxStudios/TShock.png?branch=general-devel" alt="Build Status"></a><a href="https://ci.appveyor.com/project/hakusaro/tshock"><img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status"></a><br />
<hr />
<a href="https://ci.appveyor.com/project/hakusaro/tshock">
<img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status">
</a>
<a href="https://github.com/Pryaxis/TShock/actions">
<img src="https://github.com/Pryaxis/TShock/workflows/Build%20Server/badge.svg" alt="GitHub Actions Build Status">
</a>
<a href="#contributors">
<img src="https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square" alt="All contributors">
</a>
<br/><br/>
<a href="https://github.com/Pryaxis/TShock/blob/general-devel/README_cn.md">查看中文版</a>
</p>
TShock is a server modification for Terraria, written in C#, and based upon the [Terraria Server API](https://github.com/NyxStudios/TerrariaAPI-Server). It uses JSON for configuration management, and offers several features not present in the Terraria Server normally.
TShock is a toolbox for Terraria servers and communities. That toolbox is jam packed with anti-cheat tools, server-side characters, groups, permissions, item bans, tons of commands, and limitless potential. It's one of a kind.
## :star: Quick Start
**We are currently updating for Terraria 1.4.0.1 (curRelease = 225). For updates, check our [twitter, @Pryaxis](https://twitter.com/Pryaxis).**
https://tshock.readme.io/docs/getting-started
* Download: [Stable](https://github.com/TShock/TShock/releases) or [Experimental](#experimental-downloads).
* Read [the documentation](https://tshock.readme.io/) to quickly get up to speed.
* Join [Discord](https://discord.gg/Cav9nYX).
* Join [Telegram](https://t.me/pryaxis).
* Download [other plugins](https://tshock.co/xf/index.php?resources/) to supercharge your server.
## Features
----
* MySQL support
* Permissions
* Multiple administrators
* Anti-cheat
* User registration
* Reserved slots
* User punishment (kicking, banning, muting)
* Server side characters
* JSON based configuration management
## Table of Contents
## Community
* [New to TShock?](#new-to-tshock)
* [Experimental Downloads](#experimental-downloads)
* [Developer's Guide](#developers-guide)
* [Background](#background)
* [Building](#building)
* [On Windows](#on-windows)
* [The Terraria Server API](#the-terraria-server-api)
* [TShock](#tshock)
* [On macOS](#on-macos)
* [On Linux](#on-linux)
* [On Unix](#on-unix)
* [The Terraria Server API](#the-terraria-server-api-1)
* [TShock](#tshock-1)
* [Working with Terraria](#working-with-terraria)
* [Code of Conduct](#code-of-conduct)
Feeling like helping out? Want to find an awesome server? Some awesome plugins?
## New to TShock?
* [Website & Forums](https://tshock.co/xf/)
* [Contribute to our docs on readme.io](https://tshock.readme.io/)
* [Join our Discord chat (supports Android, iOS, Web, Mac, and Windows)](https://discord.gg/XUJdH58)
_These instructions assume Windows. If you're setting up on Linux or macOS, please refer to [the in-depth guide](https://tshock.readme.io/docs/getting-started) (and don't forget to install the *latest version* of `mono-complete` on Linux)._
### Code of Conduct
1. Download [the latest stable version](https://github.com/TShock/TShock/releases) and `unzip` the folder using your favorite unzip tool. Make sure that all of the files in the zip get into one folder. This is where your server will be stored. The file structure looks like this:
GeoIP.dat
Newtonsoft.Json.dll
OTAPI.dll
ServerPlugins\
|------BCrypt.Net.dll
|------HttpServer.dll
|------Mono.Data.Sqlite.dll
|------MySql.Data.dll
|------TShockAPI.dll
TerrariaServer.exe
sqlite3.dll
1. Start `TerrariaServer.exe` and TShock will boot. Answer the startup questions, and you should be ready to roll. In the background, TShock made some folders for you. We'll come back to those later.
1. Startup Terraria. Connect to a `multiplayer` server via IP and enter `localhost` if you're doing this on your local computer. If you're doing it on another computer, you need its IP address.
1. Look at the server console for the _setup code_. Type `/setup [code]` (example: `/setup 12345`), then a space, then the code you see in the console in your game chat. Instead of chatting, you'll run a command on the server. This one makes you temporary admin. All commands are prefixed with `/` or `!` (to make them silent).
1. Use the in-game command `/user add [account name] [password] owner` (example: `/user add shank lovely-ashes owner`) to create an account. This gives you owner rights on your server, which you can configure more to your liking later.
1. Login to your newly created account with `/login [account name] [password]` (example: `/login shank lovely-ashes`). You should see a login success message.
1. Turn off the setup system with `/setup` and your server is setup for initial use. TShock also created several files inside a new `tshock` folder. These files include `config.json` (our big configuration file), `sscconfig.json` (the server side characters configuration file), and `tshock.sqlite`. Don't lose your `tshock.sqlite` or you'll have to re-setup TShock.
1. You can now [customize your configuration](https://tshock.readme.io/docs/config-settings), build groups, ban items, and install more plugins.
## Experimental Downloads
To download experimental versions of TShock, you have two real options: AppVeyor builds or GitHub builds. You can also get archived Travis CI builds. Fair warning though: experimental versions of TShock are point-in-time releases that are not technically supported by us. If you have to report an issue, please make it clear which commit or branch you downloaded your build from, which service, and the build number if applicable.
On [AppVeyor](https://ci.appveyor.com/project/hakusaro/tshock/), click on history, find the build you want, click on the commit message, and then click on the artifacts tab. You can download either the debug or the release build. AppVeyor only keeps builds back 6 months though.
On [GitHub](https://github.com/Pryaxis/TShock/), click on the actions tab, then click on "build server" on the commit or branch you want. If it was successful, you can download either the experimental release or debug artifacts.
For old builds from Travis CI, you can still get them (for now) from us directly, on [our Travis CI artifact mirror](https://travis.tshock.co/). Please note that these builds should be considered legacy and for archival purposes only. If you need them in the long term, please raise an issue explaining why before they are removed.
## Developer's Guide
Whether you want to contribute to TShock by sending a pull request, customize it to suit your own elvish desires, or want to build your own plugin, this is the best starting point. By the end of this, you'll be able to build TShock from source, start to finish. More than that, though, you'll know how to start on the path of becoming an expert TShock developer.
But first, you need some background.
### Background
Terraria is a C# application written on the .NET framework using the XNA game framework. TShock is a mod for Terraria's server, which is also written in C# on the .NET framework. Some might compare TShock to hMod in the Minecraft world (the precursor to Bukkit and its server, CraftBukkit). This is a good comparison to make in how the underlying build process works. When the project started, TShock was injected directly into the decompiled source code for Terraria. Unlike Minecraft, Terraria is not obfuscated, which means that many variable names and inner workings are sanely-named out of the box. Now, TShock uses advanced techniques to operate.
TShock is, first and foremost, a plugin written for the server variant of the Terraria API, an unofficial construct originally built by `bladecoding`. `TShock` has been colloquially used to refer to both the plugin as well as the server and plugin together. Similarly, the Terraria API's client version was abandoned long ago, and development of the `Server` API led to the abbreviation `TSAPI`, for `Terraria Server API`. The plugin `TShock` is executed by the [Terraria Server API](https://github.com/Pryaxis/TerrariaAPI-Server), which is in turn bound to the `Open Terraria API`, more commonly `OTAPI`. The [Open Terraria API](https://github.com/DeathCradle/Open-Terraria-API) is maintained by [DeathCradle](https://github.com/DeathCradle).
Now, the way that `TShock` runs on `TSAPI` through `OTAPI` can be summarized as the following:
1. The Open Terraria API deeply integrates with Terraria by modifying the official server's binary directly. This is done through rewriting the Terraria bytecode, the [CIL code](https://en.wikipedia.org/wiki/Common_Intermediate_Language), using a patching tool designed by DeathCradle and tools from the Mono project. For `TSAPI`, additional modifications are done to support TSAPI specific features. This done through the `TShock Mintaka Patcher`.
2. The `Terraria Server API` uses hooks provided by `OTAPI` to provide higher level hooks as well as legacy hooks for existing TSAPI applications.
3. `TShock` is executed by `TSAPI`, uses hooks provided by both `TSAPI` and `OTAPI`, and provides even higher level hooks and support tools to other `TSAPI` plugins.
With all of this in mind, the primary goal when compiling TShock is to remember that only the second and third layers are required to be interacted with. The first layer, `OTAPI`, is provided pre-compiled through NuGet. The second layer, `TSAPI`, is provided in the `TShock` repository through a git submodule. Its primary home is the [Terraria Server API repository](https://github.com/Pryaxis/TerrariaAPI-Server).
Let's get started.
### Building
You need to get the source code. Using git, [clone this repository](https://help.github.com/articles/cloning-a-repository/).
The next set of instructions are the technical details to setup both the Terraria Server API and TShock. More importantly, the Terraria API steps here are written under the assumption that you are building TShock primarily. Before you start, you need to **initialize the git submodules** and then **update them**. You need to use the following commands to do this.
$ git submodule init
$ git submodule update
If you're using [GitHub Desktop](https://desktop.github.com), you need to perform additional steps. After cloning the TShock repository, go to the `Repository` menu and select `Open in Command Prompt`. If you don't have Git (not GitHub Desktop) installed, you can follow the prompts to to install Git for your command line. Once Git is installed, use this same process to get to the command prompt. Then, run the above commands.
#### On Windows
On Windows, you need to install [Visual Studio Community Edition](https://www.visualstudio.com/downloads/) or a better (more expensive) version of Visual Studio.
##### The Terraria Server API
1. Open the `TShock.4.OTAPI.sln` solution in the `TerrariaServerAPI` folder.
1. Set the `TShock.Modifications.Bootstrapper` project as the StartUp project.
1. Build the solution in either debug or release mode, depending on your preference. NuGet will automatically fetch the appropriate packages as a result of its magical powers.
1. Hit the "Start" button in Visual Studio to run the `TShock Mintaka Bootstrapper`.
1. Watch the output window and make sure that a non-zero number of modifications ran. When it completes, you have successfully bootstrapped `TShock Mintaka`.
1. Set the `TerrariaServerAPI` project as the StartUp project.
1. Build the solution in either debug or release mode, depending on your preference.
1. Close `TShock.4.OTAPI.sln` in Visual Studio.
You need to re-run the patcher any time `OTAPI` updates. You need to rebuild `TerrariaServerAPI` any time that the submodule in `TShock` gets changed, if you're doing this from inside the TShock repo. You also need to update the submodules (`git submodule update`) if they're out of date on a pull too.
##### TShock
1. Open the `TShock.sln` solution in the root of the repository.
1. Build the solution. It should correctly download NuGet packages automatically and build against the aforementioned `TerrariaServerAPI` project you just built.
#### On macOS
1. Install [Homebrew](https://brew.sh) if you haven't already.
1. Install mono:
$ brew install mono
1. Verify that mono is available:
$ mono --version
Mono JIT compiler version 5.0.1.1 (2017-02/5077205 Sun Sep 17 18:29:46 BST 2017)
...
1. Proceed to the [unix build steps](#unix-build-steps) to continue.
#### On Linux
1. **DO NOT** just install mono from your package manager unless told to do so. If you do and it's out of date, you probably won't be able to successfully develop for TShock.
1. Follow the [official install instructions for mono](http://www.mono-project.com/download/). **DO** install `mono-complete` or you're missing components.
1. Proceed to the [unix build steps](#unix-build-steps) to continue.
#### On Unix
1. You need to get NuGet. Download the latest `nuget.exe` from [NuGet](https://www.nuget.org/downloads).
1. Make a `~/bin` folder if you don't have one. Then, put `nuget.exe` inside it.
$ mkdir ~/bin/
$ cp ~/downloads/nuget.exe ~/bin/
1. Set an environment variable to store if you plan to build in debug or release.
$ export BUILD_MODE=Debug
or
$ export BUILD_MODE=Release
##### The Terraria Server API
1. Perform a NuGet restore in the directory above `TerrariaServerAPI`.
$ mono ~/bin/nuget.exe restore ./TerrariaServerAPI/
1. Build the `TShock.4.OTAPI.sln` solution the configuration you chose:
$ xbuild ./TerrariaServerAPI/TShock.4.OTAPI.sln /p:Configuration=$BUILD_MODE
1. Run the `TShock Mintaka Bootstrapper` with the TShock modifications. If you don't use `/bin/bash` as your primary shell, you might want to temporarily switch to it, or the bootstrapper may fail.
$ cd ./TerrariaServerAPI/TShock.Modifications.Bootstrapper/bin/$BUILD_MODE/
$ mono TShock.Modifications.Bootstrapper.exe -in=OTAPI.dll \
-mod=../../../TShock.Modifications.**/bin/$BUILD_MODE/TShock.Modifications.*.dll \
-o=Output/OTAPI.dll
1. Verify that non-zero modifications ran successfully. Then, build the Terraria Server API executable.
$ cd ./../../../
$ xbuild ./TerrariaServerAPI/TerrariaServerAPI/TerrariaServerAPI.csproj \
/p:Configuration=$BUILD_MODE
You need to re-run the patcher any time `OTAPI` updates. You need to rebuild `TerrariaServerAPI` any time that the submodule in `TShock` gets changed, if you're doing this from inside the TShock repo. You also need to update the submodules (`git submodule update`) if they're out of date on a pull too.
##### TShock
1. Perform a NuGet restore in `TShockAPI` folder that contains `TShockAPI.sln`.
$ mono ~/bin/nuget.exe restore
1. Build TShock in the `BUILD_MODE` you set earlier.
$ xbuild ./TShockAPI.sln /p:Configuration=$BUILD_MODE
You're done!
### Working with Terraria
Working with Terraria in TShock and in other Terraria Server API plugins is different from most other APIs. Due to the nature of how OTAPI works, you have direct access to all public fields in the `Terraria` namespace. This means that you can access Terraria member methods directly. TShock and other plugins do this quite often, mostly to modify the game world, send data, and receive data. Calls to `Main` are one such example of direct access to Terraria. This is the equivalent to `net.minecraft.server` (NMS) calls in CraftBukkit.
You might find yourself wondering where these fields are. Pryaxis provides the decompiled [Sources](https://github.com/pryaxis/Sources) to Terraria's server, updated with each release. Note that these decompiled servers do not re-compile. The process of fixing the decompiles has proven to be nearly impossible in a reasonable timeframe with the modern Terraria Server.
Finally, you may be interested in developing other Terraria Server API plugins. The [TShockResources](https://github.com/TShockResources) organization has several plugins you can look at and build on. TShock is itself a plugin, and most plugins are open source. This gives you ample room to figure out where to go next.
Need help? Join us on [Telegram](https://t.me/pryaxis) or [Discord](https://discord.gg/Cav9nYX).
## Code of Conduct
> By participating in the TShock for Terraria community, all members will adhere to maintaining decorum with respect to all humans, in and out of the community. Members will not engage in discussion that inappropriately disparages or marginalizes any group of people or any individual. Members will not attempt to further or advance an agenda to the point of being overbearing or close minded (such as through spreading FUD). Members will not abuse services provided to them and will follow the guidance of community leaders on a situational basis about what abuse consists of. Members will adhere to United States and international law. If members notice a violation of this code of conduct, they will not engage but will instead contact the leadership team on either the forums or Discord.
> Do not attempt to circumvent or bypass the code of conduct by using clever logic or reasoning (e.g., insulting Facepunch members, because they weren't directly mentioned here).
Please see the contributing file before sending pull requests.
## Contributors
## Download
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
* [Github Releases](https://github.com/TShock/TShock/releases)
* [Development Builds](https://travis.tshock.co/)
* [Plugins](https://tshock.co/xf/index.php?resources/)
* [Very, very old versions of TShock](https://github.com/TShock/TShock/downloads)
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://avikav.net"><img src="https://avatars2.githubusercontent.com/u/18518861?v=4" width="100px;" alt=""/><br /><sub><b>AviKav</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/issues?q=author%3AAviKav" title="Bug reports">🐛</a> <a href="https://github.com/Pryaxis/TShock/commits?author=AviKav" title="Tests">⚠️</a></td>
<td align="center"><a href="https://tshock.co"><img src="https://avatars0.githubusercontent.com/u/3332657?v=4" width="100px;" alt=""/><br /><sub><b>Rodrigo Rente</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=AxisKriel" title="Code">💻</a> <a href="#projectManagement-AxisKriel" title="Project Management">📆</a> <a href="https://github.com/Pryaxis/TShock/commits?author=AxisKriel" title="Tests">⚠️</a></td>
<td align="center"><a href="https://sgkoi.dev"><img src="https://avatars2.githubusercontent.com/u/9637711?v=4" width="100px;" alt=""/><br /><sub><b>Stargazing Koishi</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=sgkoishi" title="Code">💻</a> <a href="#infra-sgkoishi" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/AxeelAnder"><img src="https://avatars2.githubusercontent.com/u/25691207?v=4" width="100px;" alt=""/><br /><sub><b>Axeel</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=AxeelAnder" title="Documentation">📖</a> <a href="#projectManagement-AxeelAnder" title="Project Management">📆</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

86
README_cn.md Normal file
View file

@ -0,0 +1,86 @@
<p align="center">
<img src="https://tshock.co/newlogo.png" alt="TShock for Terraria"><br />
<a href="https://ci.appveyor.com/project/hakusaro/tshock"><img src="https://ci.appveyor.com/api/projects/status/chhe61q227lqdlg1?svg=true" alt="AppVeyor Build Status"></a><a href="https://github.com/Pryaxis/TShock/actions"><img src="https://github.com/Pryaxis/TShock/workflows/Build%20Server/badge.svg" alt="GitHub Actions Build Status"></a><a href="#contributors"><img src="https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square" alt="All contributors"></a><br />
</p>
TShock是为泰拉瑞亚设计的多功能服务端。它拥有反作弊/强制开荒/用户组/权限管理/物品封禁/大量指令和无限的扩展性。
* 下载: [稳定版](https://github.com/TShock/TShock/releases) or [测试版](#experimental-downloads)
* 使用方法请阅读 [文档](https://tshock.readme.io/)
* 你可以加入 [我们的官方QQ群](https://jq.qq.com/?_wv=1027&k=5GJZCe4) 交流
* 也可以加入 [我们的Discord服务器](https://discord.gg/Cav9nYX) 提问
* 如果想要深度技术支持,可以加入 [我们的Telegram群](https://t.me/pryaxis)
* 你可以在 [这里](https://tshock.co/xf/index.php?resources/) 下载插件增强你的服务器
----
## 内容索引
* [第一次使用TShock?](#new-to-tshock)
* [下载测试版](#experimental-downloads)
## 第一次使用TShock?
_这篇指南基于Windows。如果你在使用Unix或者Linux请参考 [深度指南](https://tshock.readme.io/docs/getting-started) (不要忘记在你的Linux系统上安装 **最新版**`mono-complete` )._
1. 下载 [最新稳定版](https://github.com/TShock/TShock/releases) 然后解压。解压后文件所在的文件夹就是你服务器的工作目录。文件夹结构大致如下:
GeoIP.dat
Newtonsoft.Json.dll
OTAPI.dll
ServerPlugins\
|------BCrypt.Net.dll
|------HttpServer.dll
|------Mono.Data.Sqlite.dll
|------MySql.Data.dll
|------TShockAPI.dll
TerrariaServer.exe
sqlite3.dll
1. 运行 `TerrariaServer.exe` TShock就会启动了。 TShock会自动创建一些文件夹具体用途稍后讨论。
1. 启动你的游戏,选择 `多人模式` 并选择 `通过IP加入`。输入 `localhost` 或者 `127.0.0.1` 如果你的服务器和游戏运行在同一台电脑上。如果你在用其他设备开服你需要输入它的IP地址。
1. 查看服务器控制台上的 _验证码_。在游戏里打开聊天窗口输入 `/setup [验证码]` (举个例子: `/setup 12345`)然后回车。这条指令可以让你成为临时管理。 所有指令都需要以 `/` 或者 `!` 开头。
1. 在游戏里输入指令 `/user add [账号名] [密码] owner` (举个例子: `/user add 鱼鱼 真可爱 owner`) 来创建一个账号并且给这个账号服主权限。
1. 登录你刚刚创建的账号,方法是输入指令 `/login [账号名] [密码]` (举个例子: `/login 鱼鱼 真可爱`) 然后你就会看到登录成功的提示。
1. 输入指令 `/setup` 关闭初始化设置功能因为你已经搞定了。TShock会在 `tshock` 文件夹内创建数个文件。包括 `config.json` (服务器配置文件), `sscconfig.json` (强制开荒配置文件) 和 `tshock.sqlite` (服务器数据库)。不要把 `tshock.sqlite` 搞丢了,不然就白折腾了。
1. 现在你可以 [调整配置](https://tshock.readme.io/docs/config-settings) ,创建用户组,封禁物品或者安装插件了。
## 下载测试版
想下载测试版的TShock你有两个选择AppVeyor或者GitHub。你也可以获取Travis CI上的旧版本。注意: 测试版的TShock理论上不受我们的支持。如果你遇到问题需要发Issue请提前声明你的版本信息。
在 [AppVeyor](https://ci.appveyor.com/project/hakusaro/tshock/) 上点击History找到需要的版本并点击, 然后点击Artifacts就可以下载它的发布版或者调试版。AppVeyor只会保留半年内的版本。
在 [GitHub项目](https://github.com/Pryaxis/TShock/) 页面里,点击 `Actions`然后点击你想要的branch的 `build server` 就可以下载它的发布版或者调试版。
关于Travis CI上的旧版本现在还可以在 [我们的Travis CI产物镜像](https://travis.tshock.co/) 上获取。但是请注意这些旧版本已经不再受支持。
## Contributors
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://avikav.net"><img src="https://avatars2.githubusercontent.com/u/18518861?v=4" width="100px;" alt=""/><br /><sub><b>AviKav</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/issues?q=author%3AAviKav" title="Bug reports">🐛</a> <a href="https://github.com/Pryaxis/TShock/commits?author=AviKav" title="Tests">⚠️</a></td>
<td align="center"><a href="https://tshock.co"><img src="https://avatars0.githubusercontent.com/u/3332657?v=4" width="100px;" alt=""/><br /><sub><b>Rodrigo Rente</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=AxisKriel" title="Code">💻</a> <a href="#projectManagement-AxisKriel" title="Project Management">📆</a> <a href="https://github.com/Pryaxis/TShock/commits?author=AxisKriel" title="Tests">⚠️</a></td>
<td align="center"><a href="https://sgkoi.dev"><img src="https://avatars2.githubusercontent.com/u/9637711?v=4" width="100px;" alt=""/><br /><sub><b>Stargazing Koishi</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=sgkoishi" title="Code">💻</a> <a href="#infra-sgkoishi" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/AxeelAnder"><img src="https://avatars2.githubusercontent.com/u/25691207?v=4" width="100px;" alt=""/><br /><sub><b>Axeel</b></sub></a><br /><a href="https://github.com/Pryaxis/TShock/commits?author=AxeelAnder" title="Documentation">📖</a> <a href="#projectManagement-AxeelAnder" title="Project Management">📆</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

1933
TShockAPI/Bouncer.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,21 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -165,10 +183,16 @@ namespace TShockAPI.CLI
{
_source = source;
for (int i = 0; i < (source.Length - 1 == 0 ? 1 : source.Length - 1); i++)
for (int i = 0; i < (source.Length - 1 == 0 ? 1 : source.Length); i++)
{
string flag = source[i].ToLowerInvariant();
string argument = null;
if (string.IsNullOrWhiteSpace(flag))
{
continue;
}
if (i + 1 < source.Length)
{
argument = source[i + 1];

View file

@ -1,3 +1,21 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -27,506 +27,497 @@ using Rests;
namespace TShockAPI
{
/// <summary>ConfigFile - The config file class, which contains the configuration for a server that is serialized into JSON and deserialized on load.</summary>
/// <summary>The config file class, which contains the configuration for a server that is serialized into JSON and deserialized on load.</summary>
public class ConfigFile
{
/// <summary>InvasionMultiplier - The equation for calculating invasion size = 100 + (multiplier * (number of active players > 200 hp)).</summary>
[Description("The equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health)).")]
/// <summary>Determines the size of invasion events. The equation for calculating invasion size = 100 + (multiplier * (number of active players > 200 hp)).</summary>
[Description("Determines the size of invasion events.\nThe equation for calculating invasion size is 100 + (multiplier * (number of active players with greater than 200 health)).")]
public int InvasionMultiplier = 1;
/// <summary>DefaultMaximumSpawns - The default max spawns per wave.</summary>
[Description("The default maximum mobs that will spawn per wave. Higher means more mobs in that wave.")]
/// <summary>The default maximum number of mobs that will spawn per wave. Higher means more mobs in that wave.</summary>
[Description("The default maximum number of mobs that will spawn per wave. Higher means more mobs in that wave.")]
public int DefaultMaximumSpawns = 5;
/// <summary>DefaultSpawnRate - The default spawn rate.</summary>
/// <summary>The delay between waves. Lower values lead to more mobs.</summary>
[Description("The delay between waves. Lower values lead to more mobs.")]
public int DefaultSpawnRate = 600;
/// <summary>ServerPort - The configured server port.</summary>
/// <summary>The port the server runs on.</summary>
[Description("The port the server runs on.")]
public int ServerPort = 7777;
/// <summary>EnableWhitelist - boolean if the whitelist functionality should be turned on.</summary>
[Description("Enable or disable the whitelist based on IP addresses in whitelist.txt.")]
/// <summary>Enable or disable the whitelist based on IP addresses in the whitelist.txt file.</summary>
[Description("Enable or disable the whitelist based on IP addresses in the whitelist.txt file.")]
public bool EnableWhitelist;
/// <summary>InfiniteInvasion - Whether or not infinite invasion mode should be on.</summary>
[Description("Enable the ability for invasion size to never decrease. Make sure to run /invade, and note that this adds 2 million+ goblins to the spawn queue for the map.")]
/// <summary>Enables never-ending invasion events. You still need to start the event.</summary>
[Description("Enables never ending invasion events. You still need to start the event, such as with the /invade command.")]
public bool InfiniteInvasion;
/// <summary>PvPMode - The server PvP mode (normal, always, or disabled).</summary>
[Description("Set the server pvp mode. Valid types are: \"normal\", \"always\" and \"disabled\".")]
/// <summary>Sets the PvP mode. Valid types are: "normal", "always", "disabled".</summary>
[Description("Sets the PvP mode. Valid types are: \"normal\", \"always\" and \"disabled\".")]
public string PvPMode = "normal";
/// <summary>SpawnProtection - Enables the spawn protection system.</summary>
/// <summary>Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.</summary>
[Description("Prevents tiles from being placed within SpawnProtectionRadius of the default spawn.")]
public bool SpawnProtection = true;
/// <summary>SpawnProtectionRadius - The spawn protection tile radius.</summary>
[Description("Radius from spawn tile for SpawnProtection.")]
/// <summary>The tile radius around the spawn tile that is protected by the SpawnProtection setting.</summary>
[Description("The tile radius around the spawn tile that is protected by the SpawnProtection setting.")]
public int SpawnProtectionRadius = 10;
/// <summary>MaxSlots - The server's max slots.</summary>
[Description("Max slots for the server. If you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher.")]
/// <summary>Maximum number of clients connected at once. If lower than Terraria's setting, the server will kick excess connections.</summary>
[Description("Maximum number of clients connected at once.\nIf you want people to be kicked with \"Server is full\" set this to how many players you want max and then set Terraria max players to 2 higher.")]
public int MaxSlots = 8;
/// <summary>RangeChecks - Whether or not the anti-grief system based on range should be enabled.</summary>
[Description("Global protection agent for any block distance based anti-grief check.")]
/// <summary>Enable or disable anti-cheat range checks based on distance between the player and their block placements.</summary>
[Description("Enable or disable anti-cheat range checks based on distance between the player and their block placements.")]
public bool RangeChecks = true;
/// <summary>DisableBuild - Whether or not building should be enabled.</summary>
[Description("Disables any building / placing of blocks.")]
/// <summary>Disables any placing, or removal of blocks.</summary>
[Description("Disables any placing, or removal of blocks.")]
public bool DisableBuild;
/// <summary>SuperAdminChatRGB - The chat color for the superadmin group.</summary>
[Description("#.#.# = Red/Blue/Green - RGB Colors for the Admin Chat Color. Max value: 255.")]
public int[] SuperAdminChatRGB = { 255, 0, 0 };
/// <summary>The chat color for the superadmin group.</summary>
[Description("The chat color for the superadmin group.\n#.#.# = Red/Blue/Green\nMax value: 255")]
public int[] SuperAdminChatRGB = { 255, 255, 255 };
/// <summary>SuperAdminChatPrefix - The superadmin chat prefix.</summary>
[Description("Super admin group chat prefix.")]
public string SuperAdminChatPrefix = "(Admin) ";
/// <summary>The superadmin chat prefix.</summary>
[Description("The superadmin chat prefix.")]
public string SuperAdminChatPrefix = "(Super Admin) ";
/// <summary>SuperAdminChatSuffix - The superadmin chat suffix.</summary>
[Description("Super admin group chat suffix.")]
/// <summary>The superadmin chat suffix.</summary>
[Description("The superadmin chat suffix.")]
public string SuperAdminChatSuffix = "";
/// <summary>BackupInterval - The backup frequency in minutes.</summary>
[Description("Backup frequency in minutes. So, a value of 60 = 60 minutes. Backups are stored in the \\tshock\\backups folder.")]
/// <summary>The interval between backups, in minutes. Backups are stored in the tshock/backups folder.</summary>
[Description("The interval between backups, in minutes. Backups are stored in the tshock/backups folder.")]
public int BackupInterval;
/// <summary>BackupKeepFor - Backup max age in minutes.</summary>
[Description("How long backups are kept in minutes. 2880 = 2 days.")]
/// <summary>For how long backups are kept in minutes.</summary>
[Description("For how long backups are kept in minutes.\neg. 2880 = 2 days.")]
public int BackupKeepFor = 60;
/// <summary>RememberLeavePos - Whether or not to remember where an IP player was when they left.</summary>
[Description("Remembers where a player left off. It works by remembering the IP, NOT the character.\neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location. Note: Won't save after server restarts.")]
/// <summary>Remembers where a player left off, based on their IP. Does not persist through server restarts.</summary>
[Description("Remembers where a player left off, based on their IP. Does not persist through server restarts.\neg. When you try to disconnect, and reconnect to be automatically placed at spawn, you'll be at your last location.")]
public bool RememberLeavePos;
/// <summary>HardcoreOnly - Whether or not HardcoreOnly should be enabled.</summary>
[Description("Hardcore players ONLY. This means softcore players cannot join.")]
/// <summary>Prevents non-hardcore players from connecting.</summary>
[Description("Prevents non-hardcore players from connecting.")]
public bool HardcoreOnly;
/// <summary>MediumcoreOnly - Whether or not MediumCore only players should be enabled.</summary>
[Description("Mediumcore players ONLY. This means softcore players cannot join.")]
/// <summary>Prevents softcore players from connecting.</summary>
[Description("Prevents softcore players from connecting.")]
public bool MediumcoreOnly;
/// <summary>KickOnMediumcoreDeath - Whether or not to kick mediumcore players on death.</summary>
[Description("Kicks a mediumcore player on death.")]
/// <summary>Whether or not to kick mediumcore players on death.</summary>
[Description("Whether or not to kick mediumcore players on death.")]
public bool KickOnMediumcoreDeath;
/// <summary>BanOnMediumcoreDeath - Whether or not to ban mediumcore players on death.</summary>
[Description("Bans a mediumcore player on death.")]
/// <summary>Whether or not to ban mediumcore players on death.</summary>
[Description("Whether or not to ban mediumcore players on death.")]
public bool BanOnMediumcoreDeath;
/// <summary>AutoSave - Whether or not to use Terraria's built-in world auto save.</summary>
[Description("Enable/disable Terraria's built in auto save.")]
/// <summary>Enable or disable Terraria's built-in world auto save.</summary>
[Description("Enable or disable Terraria's built-in world auto save.")]
public bool AutoSave = true;
/// <summary>AnnounceSave - Whether or not to broadcast world saves.</summary>
[Description("Enable/disable save announcements.")]
/// <summary>Enable or disable world save announcements.</summary>
[Description("Enable or disable world save announcements.")]
public bool AnnounceSave = true;
/// <summary>MaximumLoginAttempts - Number of failed login attempts before kicking a player.</summary>
/// <summary>Number of failed login attempts before kicking the player.</summary>
[Description("Number of failed login attempts before kicking the player.")]
public int MaximumLoginAttempts = 3;
/// <summary>ServerName - Used when replying to a REST /status request or sent to the client.</summary>
/// <summary>Replaces the world name during a session if UseServerName is true.</summary>
[Description("Replaces the world name during a session if UseServerName is true.")]
public string ServerName = "";
/// <summary>UseServerName - Whether or not to use ServerName in place of the world name.</summary>
[Description("Sends ServerName in place of the world name to clients.")]
/// <summary>Whether or not to use ServerName in place of the world name.</summary>
[Description("Whether or not to use ServerName in place of the world name.")]
public bool UseServerName = false;
/// <summary>StorageType - The type of SQL database to use when storing data (either "sqlite" or "mysql").</summary>
[Description("Valid types are \"sqlite\" and \"mysql\".")]
/// <summary>The type of database to use when storing data (either "sqlite" or "mysql").</summary>
[Description("The type of database to use when storing data (either \"sqlite\" or \"mysql\").")]
public string StorageType = "sqlite";
/// <summary>MySqlHost - The hostname and port to to use when connecting to a MySQL database.</summary>
/// <summary>The MySQL hostname and port to direct connections to.</summary>
[Description("The MySQL hostname and port to direct connections to.")]
public string MySqlHost = "localhost:3306";
/// <summary>MySqlDbName - The database name to use when connecting to a MySQL database.</summary>
[Description("Database name to connect to.")]
/// <summary>The database name to connect to when using MySQL as the database type.</summary>
[Description("The database name to connect to when using MySQL as the database type.")]
public string MySqlDbName = "";
/// <summary>MySqlUsername - The username for the login credentials used when connecting to a MySQL database.</summary>
[Description("Database username to connect with.")]
/// <summary>The username used when connecting to a MySQL database.</summary>
[Description("The username used when connecting to a MySQL database.")]
public string MySqlUsername = "";
/// <summary>MySqlPassword - The password for the login credentials used when connecting to a MySQL database.</summary>
[Description("Database password to connect with.")]
/// <summary>The password used when connecting to a MySQL database.</summary>
[Description("The password used when connecting to a MySQL database.")]
public string MySqlPassword = "";
/// <summary>MediumcoreBanReason - The reason given if banning mediumcore players on death.</summary>
[Description("The reason given when banning a mediumcore player on death if BanOnMediumcoreDeath is set to true.")]
/// <summary>The reason given if banning a mediumcore player on death.</summary>
[Description("The reason given if banning a mediumcore player on death.")]
public string MediumcoreBanReason = "Death results in a ban";
/// <summary>MediumcoreKickReason - The reason given if kicking mediumcore players on death.</summary>
[Description("The reason given when kicking a mediumcore player on death if KickOnMediumcoreDeath is set to true.")]
/// <summary>The reason given if kicking a mediumcore players on death.</summary>
[Description("The reason given if kicking a mediumcore players on death.")]
public string MediumcoreKickReason = "Death results in a kick";
/// <summary>EnableIPBans - Whether or not to kick players on join that match a banned IP address.</summary>
[Description("Enables kicking of banned users by matching their IP Address.")]
/// <summary>Enables kicking banned users by matching their IP Address.</summary>
[Description("Enables kicking banned users by matching their IP Address.")]
public bool EnableIPBans = true;
/// <summary>EnableUUIDBans - Whether or not to kick players on join that match a banned UUID.</summary>
[Description("Enables kicking of banned users by matching their client UUID.")]
/// <summary>Enables kicking banned users by matching their client UUID.</summary>
[Description("Enables kicking banned users by matching their client UUID.")]
public bool EnableUUIDBans = true;
/// <summary>EnableBanOnUsernames - Whether or not to kick players on join that match a banned character name.</summary>
[Description("Enables kicking of banned users by matching their Character Name.")]
/// <summary>Enables kicking banned users by matching their Character Name.</summary>
[Description("Enables kicking banned users by matching their Character Name.")]
public bool EnableBanOnUsernames;
/// <summary>DefaultRegistrationGroupName - The default group name to place newly registered users under.</summary>
[Description("Selects the default group name to place new registrants under.")]
/// <summary>The default group name to place newly registered users under.</summary>
[Description("The default group name to place newly registered users under.")]
public string DefaultRegistrationGroupName = "default";
/// <summary>DefaultGuestGroupName - The default group name to place unregistered players under.</summary>
[Description("Selects the default group name to place unregistered players under.")]
/// <summary>The default group name to place unregistered players under.</summary>
[Description("The default group name to place unregistered players under.")]
public string DefaultGuestGroupName = "guest";
/// <summary>DisableSpewLogs - Whether or not to send logs as messages to players with the log permission.</summary>
[Description("Force-disable printing logs to players with the log permission.")]
/// <summary>Disables sending logs as messages to players with the log permission.</summary>
[Description("Disables sending logs as messages to players with the log permission.")]
public bool DisableSpewLogs = true;
/// <summary>DisableSecondUpdateLogs - Prevents OnSecondUpdate() checks from writing to the log file.</summary>
/// <summary>Prevents OnSecondUpdate checks from writing to the log file.</summary>
[Description("Prevents OnSecondUpdate checks from writing to the log file.")]
public bool DisableSecondUpdateLogs = false;
/// <summary>HashAlgorithm - The hash algorithm used to encrypt user passwords.
/// <summary>The hash algorithm used to encrypt user passwords.
/// Valid types: "sha512", "sha256" and "md5". Append with "-xp" for the xp supported algorithms.</summary>
[Description("The hash algorithm used to encrypt user passwords. Valid types: \"sha512\", \"sha256\" and \"md5\". Append with \"-xp\" for the xp supported algorithms.")]
public string HashAlgorithm = "sha512";
/// <summary>ServerFullReason - The reason given when kicking players when the server is full.</summary>
[Description("String that is used when kicking people when the server is full.")]
/// <summary>The reason given when kicking players that attempt to join while the server is full.</summary>
[Description("The reason given when kicking players that attempt to join while the server is full.")]
public string ServerFullReason = "Server is full";
/// <summary>WhitelistKickReason - The reason given when kicking players for not being on the whitelist.</summary>
[Description("String that is used when a user is kicked due to not being on the whitelist.")]
/// <summary>The reason given when kicking players for not being on the whitelist.</summary>
[Description("The reason given when kicking players for not being on the whitelist.")]
public string WhitelistKickReason = "You are not on the whitelist.";
/// <summary>ServerFullNoReservedReason - The reason given when kicking players when the server is full and there are no reserved slots open.</summary>
[Description("String that is used when kicking people when the server is full with no reserved slots.")]
/// <summary>The reason given when kicking players that attempt to join while the server is full with no reserved slots available.</summary>
[Description("The reason given when kicking players that attempt to join while the server is full with no reserved slots available.")]
public string ServerFullNoReservedReason = "Server is full. No reserved slots open.";
/// <summary>SaveWorldOnCrash - Attempts to save world in the server crashes due to an unhandled exception.</summary>
[Description("This will save the world if Terraria crashes from an unhandled exception.")]
/// <summary>Whether or not to save the world if the server crashes from an unhandled exception.</summary>
[Description("Whether or not to save the world if the server crashes from an unhandled exception.")]
public bool SaveWorldOnCrash = true;
/// <summary>EnableGeoIP - Whether or not to announce a player's location on join.</summary>
[Description("This will announce a player's location on join.")]
/// <summary>Whether or not to announce a player's geographic location on join, based on their IP.</summary>
[Description("Whether or not to announce a player's geographic location on join, based on their IP.")]
public bool EnableGeoIP;
/// <summary>EnableTokenEndpointAuthentication - Whether or not to require token authentication for the public REST API endpoints.</summary>
[Description("This will turn on token requirement for the public REST API endpoints.")]
/// <summary>Whether or not to require token authentication to use the public REST API endpoints.</summary>
[Description("Whether or not to require token authentication to use the public REST API endpoints.")]
public bool EnableTokenEndpointAuthentication;
/// <summary>RestApiEnabled - Enable/disable the REST API.</summary>
[Description("Enable/disable the REST API.")]
/// <summary>Enable or disable the REST API.</summary>
[Description("Enable or disable the REST API.")]
public bool RestApiEnabled;
/// <summary>RestApiPort - The port used by the REST API.</summary>
[Description("This is the port which the REST API will listen on.")]
/// <summary>The port used by the REST API.</summary>
[Description("The port used by the REST API.")]
public int RestApiPort = 7878;
/// <summary>DisableTombstones - Disable tombstone dropping during death for all players.</summary>
[Description("Disable tombstone dropping during death for all players.")]
/// <summary>Disables tombstone dropping during death for all players.</summary>
[Description("Disables tombstone dropping during death for all players.")]
public bool DisableTombstones = true;
/// <summary>DisplayIPToAdmins - Displays a player's IP on join to everyone with the log permission.</summary>
[Description("Displays a player's IP on join to everyone who has the log permission.")]
/// <summary>Displays a player's IP on join to users with the log permission.</summary>
[Description("Displays a player's IP on join to users with the log permission.")]
public bool DisplayIPToAdmins;
/// <summary>KickProxyUsers - If the GeoIP service is running, this will kick users under a proxy.</summary>
[Description("Kicks users using a proxy as identified with the GeoIP database.")]
/// <summary>If GeoIP is enabled, this will kick users identified as being under a proxy.</summary>
[Description("If GeoIP is enabled, this will kick users identified as being under a proxy.")]
public bool KickProxyUsers = true;
/// <summary>DisableHardmode - If set to true, hardmode will not be activated by the Wall of Flesh or the /starthardmode command.</summary>
[Description("Disables hardmode, can't never be activated. Overrides /starthardmode.")]
/// <summary>If enabled, hardmode will not be activated by the Wall of Flesh or the /starthardmode command.</summary>
[Description("If enabled, hardmode will not be activated by the Wall of Flesh or the /starthardmode command.")]
public bool DisableHardmode;
/// <summary>DisableDungeonGuardian - Disables the dungeon guardian from being spawned while sending players to their spawn point instead.</summary>
[Description("Disables the dungeon guardian from being spawned while sending players to their spawn point instead.")]
/// <summary>Prevents the dungeon guardian from being spawned while sending players to their spawn point instead.</summary>
[Description("Prevents the dungeon guardian from being spawned while sending players to their spawn point instead.")]
public bool DisableDungeonGuardian;
/// <summary>DisableClownBombs - Disables clown bomb projectiles from spawning.</summary>
/// <summary>Disables clown bomb projectiles from spawning.</summary>
[Description("Disables clown bomb projectiles from spawning.")]
public bool DisableClownBombs;
/// <summary>DisableSnowBalls - Disables snow ball projectiles from spawning.</summary>
/// <summary>Disables snow ball projectiles from spawning.</summary>
[Description("Disables snow ball projectiles from spawning.")]
public bool DisableSnowBalls;
/// <summary>ChatFormat - Controls the in-game chat format. {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message.</summary>
/// <summary>Changes in-game chat format: {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message.</summary>
[Description("Changes in-game chat format: {0} = Group Name, {1} = Group Prefix, {2} = Player Name, {3} = Group Suffix, {4} = Chat Message.")]
public string ChatFormat = "{1}{2}{3}: {4}";
/// <summary>ChatAboveHeadsFormat - Modifies the player name when using chat above heads. Same formatting options as ChatFormat.</summary>
[Description("Change the player name when using chat above heads. This begins with a player name wrapped in brackets, as per Terraria's formatting. Same formatting as ChatFormat(minus the text aka {4}).")]
/// <summary>Changes the player name when using chat above heads. Starts with a player name wrapped in brackets, as per Terraria's formatting.\nSame formatting as ChatFormat without the message.</summary>
[Description("Changes the player name when using chat above heads. Starts with a player name wrapped in brackets, as per Terraria's formatting.\nSame formatting as ChatFormat without the message.")]
public string ChatAboveHeadsFormat = "{2}";
/// <summary>ForceTime - Can be either "normal", "day" or "night". When set to one of the latter two, the blocks other.</summary>
[Description("Force the world time to be normal, day, or night.")]
/// <summary>Forces the world time to be normal, day, or night.</summary>
[Description("Forces the world time to be normal, day, or night.")]
public string ForceTime = "normal";
/// <summary>TileKillThreshold - Disables/reverts a player if this number of tile kills is exceeded within 1 second.</summary>
[Description("Disables/reverts a player if this number of tile kills is exceeded within 1 second.")]
/// <summary>Disables a player and reverts their actions if this number of tile kills is exceeded within 1 second.</summary>
[Description("Disables a player and reverts their actions if this number of tile kills is exceeded within 1 second.")]
public int TileKillThreshold = 60;
/// <summary>TilePlaceThreshold - Disables/reverts a player if this number of tile placements is exceeded within 1 second.</summary>
[Description("Disables/reverts a player if this number of tile places is exceeded within 1 second.")]
/// <summary>Disables a player and reverts their actions if this number of tile places is exceeded within 1 second.</summary>
[Description("Disables a player and reverts their actions if this number of tile places is exceeded within 1 second.")]
public int TilePlaceThreshold = 20;
/// <summary>TileLiquidThreshold - Disables a player if this number of liquid sets is exceeded within 1 second.</summary>
/// <summary>Disables a player if this number of liquid sets is exceeded within 1 second.</summary>
[Description("Disables a player if this number of liquid sets is exceeded within 1 second.")]
public int TileLiquidThreshold = 15;
/// <summary>ProjectileThreshold - Disables a player if this number of projectiles is created within 1 second.</summary>
/// <summary>Disable a player if this number of projectiles is created within 1 second.</summary>
[Description("Disable a player if this number of projectiles is created within 1 second.")]
public int ProjectileThreshold = 50;
/// <summary>HealOtherThreshold - Disables a player if this number of HealOtherPlayer packets is sent within 1 second.</summary>
/// <summary>Disables a player if this number of HealOtherPlayer packets is sent within 1 second.</summary>
[Description("Disables a player if this number of HealOtherPlayer packets is sent within 1 second.")]
public int HealOtherThreshold = 50;
/// <summary>ProjIgnoreShrapnel - Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count.</summary>
[Description("Ignore shrapnel from crystal bullets for projectile threshold.")]
/// <summary>Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count.</summary>
[Description("Whether or not to ignore shrapnel from crystal bullets for the projectile threshold count.")]
public bool ProjIgnoreShrapnel = true;
/// <summary>RequireLogin - Requires all players to register or login before being allowed to play.</summary>
[Description("Requires all players to register or login before being allowed to play.")]
/// <summary>Require all players to register or login before being allowed to play.</summary>
[Description("Require all players to register or login before being allowed to play.")]
public bool RequireLogin;
/// <summary>DisableInvisPvP - Whether or not to turn a player invisible if using invisibility potions during PvP.</summary>
[Description("Disables invisibility potions from being used in PvP (Note, can be used in the client, but the effect isn't sent to the rest of the server).")]
/// <summary>Disables the effect of invisibility potions while PvP is enabled by turning the player visible to the other clients.</summary>
[Description("Disables the effect of invisibility potions while PvP is enabled by turning the player visible to the other clients.")]
public bool DisableInvisPvP;
/// <summary>MaxRangeForDisabled - The maximum distance, in tiles, that disabled players can move from.</summary>
[Description("The maximum distance players disabled for various reasons can move from.")]
/// <summary>The maximum distance, in tiles, that disabled players can move from.</summary>
[Description("The maximum distance, in tiles, that disabled players can move from.")]
public int MaxRangeForDisabled = 10;
/// <summary>ServerPassword - The server password required to join the server.</summary>
[Description("Server password required to join the server.")]
/// <summary>The server password required to join the server.</summary>
[Description("The server password required to join the server.")]
public string ServerPassword = "";
/// <summary>RegionProtectChests - Whether or not region protection should apply to chests.</summary>
[Description("Protect chests with region and build permissions.")]
/// <summary>Whether or not region protection should apply to chests.</summary>
[Description("Whether or not region protection should apply to chests.")]
public bool RegionProtectChests;
/// <summary>RegionProtectGemLocks - Whether or not region protection should apply to gem locks.</summary>
[Description("Protect gem locks with region and build permissions.")]
/// <summary>Whether or not region protection should apply to gem locks.</summary>
[Description("Whether or not region protection should apply to gem locks.")]
public bool RegionProtectGemLocks = true;
/// <summary>DisableLoginBeforeJoin - This will prevent users from being able to login before connecting.</summary>
[Description("Disable users from being able to login with account password when joining.")]
/// <summary>Prevents users from being able to login before they finish connecting.</summary>
[Description("Prevents users from being able to login before they finish connecting.")]
public bool DisableLoginBeforeJoin;
/// <summary>DisableUUIDLogin - This will disable automatic login through a saved client UUID.</summary>
[Description("Disable users from being able to login with their client UUID.")]
/// <summary>Prevents users from being able to login with their client UUID.</summary>
[Description("Prevents users from being able to login with their client UUID.")]
public bool DisableUUIDLogin;
/// <summary>KickEmptyUUID - Kick clients that don't send a UUID to the server.</summary>
[Description("Kick clients that don't send a UUID to the server.")]
/// <summary>Kick clients that don't send their UUID to the server.</summary>
[Description("Kick clients that don't send their UUID to the server.")]
public bool KickEmptyUUID;
/// <summary>AllowRegisterAnyUsername - Allows users to register a username that doesn't necessarily match their character name.</summary>
[Description("Allows users to register any username with /register.")]
/// <summary>Allows users to register a username that doesn't necessarily match their character name.</summary>
[Description("Allows users to register a username that doesn't necessarily match their character name.")]
public bool AllowRegisterAnyUsername;
/// <summary>AllowLoginAnyUsername - Allows users to login to any account even if the username doesn't match their character name.</summary>
[Description("Allows users to login with any username with /login.")]
/// <summary>Allows users to login to any account even if the username doesn't match their character name.</summary>
[Description("Allows users to login to any account even if the username doesn't match their character name.")]
public bool AllowLoginAnyUsername = true;
/// <summary>The maximum damage a player/NPC can inflict.</summary>
[Description("The maximum damage a player/npc can inflict.")]
[Description("The maximum damage a player/NPC can inflict.")]
public int MaxDamage = 1175;
/// <summary>The maximum damage a projectile can inflict.</summary>
[Description("The maximum damage a projectile can inflict.")]
public int MaxProjDamage = 1175;
/// <summary>KickOnDamageThresholdBroken - Whether or not to kick users when they surpass the MaxDamage threshold.</summary>
[Description("Kicks a user if set to true, if they inflict more damage then the max damage.")]
/// <summary>Whether or not to kick users when they surpass the MaxDamage threshold.</summary>
[Description("Whether or not to kick users when they surpass the MaxDamage threshold.")]
public bool KickOnDamageThresholdBroken = false;
/// <summary>IgnoreProjUpdate - Ignores checking to see if player 'can' update a projectile.</summary>
[Description("Ignores checking to see if player 'can' update a projectile.")]
/// <summary>Ignores checks to see if a player 'can' update a projectile.</summary>
[Description("Ignores checks to see if a player 'can' update a projectile.")]
public bool IgnoreProjUpdate = false;
/// <summary>IgnoreProjKill - Ignores checking to see if player 'can' kill a projectile.</summary>
[Description("Ignores checking to see if player 'can' kill a projectile.")]
/// <summary>Ignores checks to see if a player 'can' kill a projectile.</summary>
[Description("Ignores checks to see if a player 'can' kill a projectile.")]
public bool IgnoreProjKill = false;
/// <summary>IgnoreNoClip - Ignores all no clip checks for players.</summary>
[Description("Ignores all no clip checks for players.")]
public bool IgnoreNoClip = false;
/// <summary>AlllowIce - Allows ice placement even where a user cannot usually build.</summary>
[Description("Allow ice placement even when user does not have canbuild.")]
/// <summary>Allows ice placement even where a user cannot usually build.</summary>
[Description("Allows ice placement even where a user cannot usually build.")]
public bool AllowIce = false;
/// <summary>AllowCrimsonCreep - Enables or disables crimson to spread when a world is in hardmode.</summary>
[Description("Allows crimson to spread when a world is hardmode.")]
/// <summary>Allows the crimson to spread when a world is in hardmode.</summary>
[Description("Allows the crimson to spread when a world is in hardmode.")]
public bool AllowCrimsonCreep = true;
/// <summary>AllowCorruptionCreep - Enables or disables corruption to spread when a world is in hardmode.</summary>
[Description("Allows corruption to spread when a world is hardmode.")]
/// <summary>Allows the corruption to spread when a world is in hardmode.</summary>
[Description("Allows the corruption to spread when a world is in hardmode.")]
public bool AllowCorruptionCreep = true;
/// <summary>AllowHallowCreep - Enables or disables hallow to spread when a world is in hardmode.</summary>
[Description("Allows hallow to spread when a world is hardmode.")]
/// <summary>Allows the hallow to spread when a world is in hardmode.</summary>
[Description("Allows the hallow to spread when a world is in hardmode.")]
public bool AllowHallowCreep = true;
/// <summary>StatueSpawn200 - How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning.</summary>
[Description("How many things a statue can spawn within 200 pixels(?) before it stops spawning. Default = 3.")]
/// <summary>How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning.</summary>
[Description("How many NPCs a statue can spawn within 200 pixels(?) before it stops spawning.\nDefault = 3.")]
public int StatueSpawn200 = 3;
/// <summary>StatueSpawn600 - How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning.</summary>
[Description("How many things a statue can spawn within 600 pixels(?) before it stops spawning. Default = 6.")]
/// <summary>How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning.</summary>
[Description("How many NPCs a statue can spawn within 600 pixels(?) before it stops spawning.\nDefault = 6.")]
public int StatueSpawn600 = 6;
/// <summary>StatueSpawnWorld - How many NPCs a statue can spawn before it stops spawning.</summary>
[Description("How many things a statue can spawn before it stops spawning. Default = 10.")]
/// <summary>How many NPCs a statue can spawn before it stops spawning.</summary>
[Description("How many NPCs a statue can spawn before it stops spawning.\nDefault = 10.")]
public int StatueSpawnWorld = 10;
/// <summary>PreventBannedItemSpawn - Prevents banned items from being spawned with commands.</summary>
[Description("Prevent banned items from being /i or /give.")]
/// <summary>Prevent banned items from being spawned or given with commands.</summary>
[Description("Prevent banned items from being spawned or given with commands.")]
public bool PreventBannedItemSpawn = false;
/// <summary>PreventDeadModification - Prevent players from interacting with the world if dead.</summary>
[Description("Prevent players from interacting with the world if dead.")]
/// <summary>Prevent players from interacting with the world while they are dead.</summary>
[Description("Prevent players from interacting with the world while they are dead.")]
public bool PreventDeadModification = true;
/// <summary>EnableChatAboveHeads - Whether or not to display chat messages above players' heads.</summary>
[Description("Displays chat messages above players' heads, but will disable chat prefixes to compensate.")]
/// <summary>Whether or not to display chat messages above players' heads.</summary>
[Description("Whether or not to display chat messages above players' heads.")]
public bool EnableChatAboveHeads = false;
/// <summary>ForceXmas - Force Christmas-only events to occur all year.</summary>
[Description("Force Christmas-only events to occur all year.")]
/// <summary>Forces Christmas-only events to occur all year.</summary>
[Description("Forces Christmas-only events to occur all year.")]
public bool ForceXmas = false;
/// <summary>AllowAllowedGroupsToSpawnBannedItems - Allows groups on the banned item allowed list to spawn banned items even if <see cref="PreventBannedItemSpawn"/> is set to true.</summary>
[Description("Allows groups on the banned item allowed list to spawn banned items.")]
/// <summary>Allows groups on the banned item allowed list to spawn banned items even if PreventBannedItemSpawn is set to true.</summary>
[Description("Allows groups on the banned item allowed list to spawn banned items even if PreventBannedItemSpawn is set to true.")]
public bool AllowAllowedGroupsToSpawnBannedItems = false;
/// <summary>IgnoreChestStacksOnLoad - Allows stacks in chests to be beyond the stack limit during world load.</summary>
[Description("Allows stacks in chests to be beyond the stack limit.")]
/// <summary>Allows stacks in chests to go beyond the stack limit during world loading.</summary>
[Description("Allows stacks in chests to go beyond the stack limit during world loading.")]
public bool IgnoreChestStacksOnLoad = false;
/// <summary>LogPath - The path of the directory where logs should be written to.</summary>
[Description("The path of the directory where logs should be written into.")]
/// <summary>The path to the directory where logs should be written to.</summary>
[Description("The path to the directory where logs should be written to.")]
public string LogPath = "tshock";
/// <summary>UseSqlLogs - Whether or not to save logs to a SQL database instead of a text file.</summary>
[Description("Save logs to an SQL database instead of a text file. Default = false.")]
/// <summary>Whether or not to save logs to the SQL database instead of a text file.</summary>
[Description("Whether or not to save logs to the SQL database instead of a text file.\nDefault = false.")]
public bool UseSqlLogs = false;
/// <summary>RevertToTextLogsOnSqlFailures - Number of times the SQL log must fail to insert logs before falling back to the text log.</summary>
/// <summary>Number of times the SQL log must fail to insert logs before falling back to the text log.</summary>
[Description("Number of times the SQL log must fail to insert logs before falling back to the text log.")]
public int RevertToTextLogsOnSqlFailures = 10;
/// <summary>PreventInvalidPlaceStyle - Prevents players from placing tiles with an invalid style.</summary>
/// <summary>Prevents players from placing tiles with an invalid style.</summary>
[Description("Prevents players from placing tiles with an invalid style.")]
public bool PreventInvalidPlaceStyle = true;
/// <summary>BroadCastRGB - The RGB values used for the color of broadcast messages.</summary>
[Description("#.#.# = Red/Blue/Green - RGB Colors for broadcasts. Max value: 255.")]
/// <summary>The RGB values used for the color of broadcast messages.</summary>
[Description("The RGB values used for the color of broadcast messages.\n#.#.# = Red/Blue/Green\nMax value: 255")]
public int[] BroadcastRGB = { 127, 255, 212 };
/// <summary>ApplicationRestTokens - A dictionary of REST tokens that external applications may use to make queries to your server.</summary>
/// <summary>A dictionary of REST tokens that external applications may use to make queries to your server.</summary>
[Description("A dictionary of REST tokens that external applications may use to make queries to your server.")]
public Dictionary<string, SecureRest.TokenData> ApplicationRestTokens = new Dictionary<string, SecureRest.TokenData>();
/// <summary>ReservedSlots - The number of reserved slots past your max server slot that can be joined by reserved players.</summary>
[Description("The number of reserved slots past your max server slot that can be joined by reserved players.")]
/// <summary>The number of reserved slots past your max server slots that can be joined by reserved players.</summary>
[Description("The number of reserved slots past your max server slots that can be joined by reserved players.")]
public int ReservedSlots = 20;
/// <summary>LogRest - Whether or not to log REST API connections.</summary>
[Description("Enable/disable the REST API connection log.")]
/// <summary>Whether or not to log REST API connections.</summary>
[Description("Whether or not to log REST API connections.")]
public bool LogRest = false;
/// <summary>RespawnSeconds - The number of seconds a player must wait before being respawned.</summary>
/// <summary>The number of seconds a player must wait before being respawned.</summary>
[Description("The number of seconds a player must wait before being respawned.")]
public int RespawnSeconds = 5;
/// <summary>RespawnBossSeconds - "The number of seconds a player must wait before being respawned if there is a boss nearby.</summary>
/// <summary>The number of seconds a player must wait before being respawned if there is a boss nearby.</summary>
[Description("The number of seconds a player must wait before being respawned if there is a boss nearby.")]
public int RespawnBossSeconds = 10;
/// <summary>TilePaintThreshold - Disables a player if this number of tiles is painted within 1 second.</summary>
/// <summary>Disables a player if this number of tiles is painted within 1 second.</summary>
[Description("Disables a player if this number of tiles is painted within 1 second.")]
public int TilePaintThreshold = 15;
/// <summary>ForceHalloween - Forces Halloween-only events to occur all year.</summary>
[Description("Forces your world to be in Halloween mode regardless of the data.")]
/// <summary>Forces Halloween-only events to occur all year.</summary>
[Description("Forces Halloween-only events to occur all year.")]
public bool ForceHalloween = false;
/// <summary>AllowCutTilesAndBreakables - Allows players to break temporary tiles (grass, pots, etc) even if they cannot typically build in a region.</summary>
[Description("Allows anyone to break grass, pots, etc.")]
/// <summary>Allows players to break temporary tiles (grass, pots, etc) where they cannot usually build.</summary>
[Description("Allows players to break temporary tiles (grass, pots, etc) where they cannot usually build.")]
public bool AllowCutTilesAndBreakables = false;
/// <summary>CommandSpecifier - Specifies which string starts a command.
/// <summary>Specifies which string starts a command.
/// Note: Will not function properly if the string length is bigger than 1.</summary>
[Description("Specifies which string starts a command.")]
[Description("Specifies which string starts a command.\nNote: Will not function properly if the string length is bigger than 1.")]
public string CommandSpecifier = "/";
/// <summary>CommandSilentSpecifier - Specifies which string starts a command silently.
/// <summary>Specifies which string starts a command silently.
/// Note: Will not function properly if the string length is bigger than 1.</summary>
[Description("Specifies which string starts a command silently.")]
[Description("Specifies which string starts a command silently.\nNote: Will not function properly if the string length is bigger than 1.")]
public string CommandSilentSpecifier = ".";
/// <summary>KickOnHardcoreDeath - Whether or not to kick a hardcore player on death.</summary>
[Description("Kicks a hardcore player on death.")]
/// <summary>Whether or not to kick hardcore players on death.</summary>
[Description("Whether or not to kick hardcore players on death.")]
public bool KickOnHardcoreDeath;
/// <summary>BanOnHardcoreDeath - Whether or not to ban a hardcore player on death.</summary>
[Description("Bans a hardcore player on death.")]
/// <summary>Whether or not to ban hardcore players on death.</summary>
[Description("Whether or not to ban hardcore players on death.")]
public bool BanOnHardcoreDeath;
/// <summary>HardcoreBanReason - The reason given when banning a hardcore player on death.</summary>
[Description("Bans a hardcore player on death.")]
/// <summary>The reason given when banning hardcore players on death.</summary>
[Description("The reason given when banning hardcore players on death.")]
public string HardcoreBanReason = "Death results in a ban";
/// <summary>HardcoreKickReason - The reason given when kicking a hardcore player on death.</summary>
[Description("Kicks a hardcore player on death.")]
/// <summary>The reason given when kicking hardcore players on death.</summary>
[Description("The reason given when kicking hardcore players on death.")]
public string HardcoreKickReason = "Death results in a kick";
/// <summary>AnonymousBossInvasions - Whether or not to announce boss spawning or invasion starts.</summary>
[Description("Whether bosses or invasions should be anonymously spawned.")]
/// <summary>Whether or not to announce boss spawning or invasion starts.</summary>
[Description("Whether or not to announce boss spawning or invasion starts.")]
public bool AnonymousBossInvasions = true;
/// <summary>MaxHP - The maximum allowable HP, before equipment buffs.</summary>
[Description("The maximum allowable HP, before equipment buffs.")]
/// <summary>The maximum HP a player can have, before equipment buffs.</summary>
[Description("The maximum HP a player can have, before equipment buffs.")]
public int MaxHP = 500;
/// <summary>MaxMP - The maximum allowable MP, before equipment buffs.</summary>
[Description("The maximum allowable MP, before equipment buffs.")]
/// <summary>The maximum MP a player can have, before equipment buffs.</summary>
[Description("The maximum MP a player can have, before equipment buffs.")]
public int MaxMP = 200;
/// <summary>SaveWorldOnLastPlayerExit - Whether or not to save the world when the last player disconnects.</summary>
[Description("Determines if the server should save the world if the last player exits.")]
/// <summary>Whether or not to save the world when the last player disconnects.</summary>
[Description("Whether or not to save the world when the last player disconnects.")]
public bool SaveWorldOnLastPlayerExit = true;
/// <summary>BCryptWorkFactor - Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify.
/// <summary>Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify.
/// The number of computational rounds is 2^n. Increase with caution. Range: 5-31.</summary>
[Description("Determines the BCrypt work factor to use. If increased, all passwords will be upgraded to new work-factor on verify. The number of computational rounds is 2^n. Increase with caution. Range: 5-31.")]
public int BCryptWorkFactor = 7;
/// <summary>MinimumPasswordLength - The minimum password length for new user accounts.</summary>
[Description("The minimum password length for new user accounts. Minimum value is 4.")]
/// <summary>The minimum password length for new user accounts. Can never be lower than 4.</summary>
[Description("The minimum password length for new user accounts. Can never be lower than 4.")]
public int MinimumPasswordLength = 4;
/// <summary>RESTMaximumRequestsPerInterval - The maximum REST requests in the bucket before denying requests.</summary>
/// <summary>The maximum REST requests in the bucket before denying requests. Minimum value is 5.</summary>
[Description("The maximum REST requests in the bucket before denying requests. Minimum value is 5.")]
public int RESTMaximumRequestsPerInterval = 5;
/// <summary>RESTRequestBucketDecreaseIntervalMinutes - How often in minutes the REST requests bucket is decreased by one.</summary>
/// <summary>How often in minutes the REST requests bucket is decreased by one. Minimum value is 1 minute.</summary>
[Description("How often in minutes the REST requests bucket is decreased by one. Minimum value is 1 minute.")]
public int RESTRequestBucketDecreaseIntervalMinutes = 1;
/// <summary>RESTLimitOnlyFailedLoginRequests - Whether or not to limit only the max failed login requests, or all login requests.</summary>
[Obsolete("This value is no longer used and will be removed next version.")]
[Description("Whether we should limit only the max failed login requests, or all login requests.")]
public bool RESTLimitOnlyFailedLoginRequests = true;
/// <summary>ShowBackupAutosaveMessages - Whether or not to show backup auto save messages.</summary>
[Description("Show backup autosave messages.")]
/// <summary>Whether or not to show backup auto save messages.</summary>
[Description("Whether or not to show backup auto save messages.")]
public bool ShowBackupAutosaveMessages = true;
/// <summary>
@ -622,4 +613,4 @@ namespace TShockAPI
File.WriteAllText("ConfigDescriptions.txt", sb.ToString());
}
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -46,7 +46,8 @@ namespace TShockAPI.DB
new SqlColumn("Reason", MySqlDbType.Text),
new SqlColumn("BanningUser", MySqlDbType.Text),
new SqlColumn("Date", MySqlDbType.Text),
new SqlColumn("Expiration", MySqlDbType.Text)
new SqlColumn("Expiration", MySqlDbType.Text),
new SqlColumn("AccountName", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
@ -75,7 +76,7 @@ namespace TShockAPI.DB
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip))
{
if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -104,7 +105,7 @@ namespace TShockAPI.DB
{
while (reader.Read())
{
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration")));
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration")));
}
banlist.Sort(new BanComparer(sortMethod));
@ -135,7 +136,33 @@ namespace TShockAPI.DB
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
{
if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
{
TShock.Log.Error(ex.ToString());
}
return null;
}
/// <summary>
/// Gets a ban by account name (not player/character name).
/// </summary>
/// <param name="name">The name.</param>
/// <param name="casesensitive">Whether to check with case sensitivity.</param>
/// <returns>The ban.</returns>
public Ban GetBanByAccountName(string name, bool casesensitive = false)
{
try
{
var namecol = casesensitive ? "AccountName" : "UPPER(AccountName)";
if (!casesensitive)
name = name.ToUpper();
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
{
if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -157,7 +184,7 @@ namespace TShockAPI.DB
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE UUID=@0", uuid))
{
if (reader.Read())
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
}
}
catch (Exception ex)
@ -178,7 +205,7 @@ namespace TShockAPI.DB
/// <param name="exceptions">If set to <c>true</c> enable throwing exceptions.</param>
/// <param name="banner">Banner.</param>
/// <param name="expiration">Expiration date.</param>
public bool AddBan(string ip, string name = "", string uuid = "", string reason = "", bool exceptions = false, string banner = "", string expiration = "")
public bool AddBan(string ip, string name = "", string uuid = "", string accountName = "", string reason = "", bool exceptions = false, string banner = "", string expiration = "")
{
try
{
@ -192,7 +219,7 @@ namespace TShockAPI.DB
}
else
{
return database.Query("INSERT INTO Bans (IP, Name, UUID, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4, @5, @6);", ip, name, uuid, reason, banner, DateTime.UtcNow.ToString("s"), expiration) != 0;
return database.Query("INSERT INTO Bans (IP, Name, UUID, Reason, BanningUser, Date, Expiration, AccountName) VALUES (@0, @1, @2, @3, @4, @5, @6, @7);", ip, name, uuid, reason, banner, DateTime.UtcNow.ToString("s"), expiration, accountName) != 0;
}
}
catch (Exception ex)
@ -231,6 +258,26 @@ namespace TShockAPI.DB
return false;
}
/// <summary>
/// Removes a ban by account name (not character/player name).
/// </summary>
/// <returns><c>true</c>, if ban was removed, <c>false</c> otherwise.</returns>
/// <param name="match">Match.</param>
/// <param name="casesensitive">If set to <c>true</c> casesensitive.</param>
public bool RemoveBanByAccountName(string match, bool casesensitive = true)
{
try
{
var namecol = casesensitive ? "AccountName" : "UPPER(AccountName)";
return database.Query("DELETE FROM Bans WHERE " + namecol + "=@0", casesensitive ? match : match.ToUpper()) != 0;
}
catch (Exception ex)
{
TShock.Log.Error(ex.ToString());
}
return false;
}
/// <summary>
/// Clears bans.
/// </summary>
@ -247,6 +294,20 @@ namespace TShockAPI.DB
}
return false;
}
/// <summary>Removes a ban if it has expired.</summary>
/// <param name="ban">The candidate ban to check.</param>
/// <returns>If the ban has been removed.</returns>
public bool RemoveBanIfExpired(Ban ban)
{
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (ban.ExpirationDateTime != null) && (DateTime.UtcNow >= ban.ExpirationDateTime))
{
RemoveBan(ban.IP, false, false, false);
return true;
}
return false;
}
}
/// <summary>
@ -380,6 +441,12 @@ namespace TShockAPI.DB
/// <value>The UUID</value>
public string UUID { get; set; }
/// <summary>
/// Gets or sets the account name of the ban
/// </summary>
/// <value>The account name</value>
public String AccountName { get; set; }
/// <summary>
/// Gets or sets the ban reason.
/// </summary>
@ -424,11 +491,12 @@ namespace TShockAPI.DB
/// <param name="banner">Banner.</param>
/// <param name="date">UTC ban date.</param>
/// <param name="exp">Expiration time</param>
public Ban(string ip, string name, string uuid, string reason, string banner, string date, string exp)
public Ban(string ip, string name, string uuid, string accountName, string reason, string banner, string date, string exp)
{
IP = ip;
Name = name;
UUID = uuid;
AccountName = accountName;
Reason = reason;
BanningUser = banner;
Date = date;
@ -454,10 +522,11 @@ namespace TShockAPI.DB
IP = string.Empty;
Name = string.Empty;
UUID = string.Empty;
AccountName = string.Empty;
Reason = string.Empty;
BanningUser = "";
Date = "";
Expiration = "";
BanningUser = string.Empty;
Date = string.Empty;
Expiration = string.Empty;
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -120,7 +120,7 @@ namespace TShockAPI.DB
return playerData;
}
public bool SeedInitialData(User user)
public bool SeedInitialData(UserAccount account)
{
var inventory = new StringBuilder();
@ -132,7 +132,7 @@ namespace TShockAPI.DB
try
{
database.Query("INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, spawnX, spawnY, questsCompleted) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8);",
user.ID,
account.ID,
TShock.ServerSideCharacterConfig.StartingHealth,
TShock.ServerSideCharacterConfig.StartingHealth,
TShock.ServerSideCharacterConfig.StartingMana,
@ -156,26 +156,26 @@ namespace TShockAPI.DB
/// </summary>
/// <param name="player">player to take data from</param>
/// <returns>true if inserted successfully</returns>
public bool InsertPlayerData(TSPlayer player)
public bool InsertPlayerData(TSPlayer player, bool fromCommand = false)
{
PlayerData playerData = player.PlayerData;
if (!player.IsLoggedIn)
return false;
if (player.HasPermission(Permissions.bypassssc))
if (player.HasPermission(Permissions.bypassssc) && !fromCommand)
{
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.User.Name); // Debug code
return true;
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.Account.Name); // Debug code
return false;
}
if (!GetPlayerData(player, player.User.ID).exists)
if (!GetPlayerData(player, player.Account.ID).exists)
{
try
{
database.Query(
"INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20);",
player.User.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisual), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished);
player.Account.ID, playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), playerData.extraSlot, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.skinVariant, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor),TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisual), TShock.Utils.EncodeColor(player.TPlayer.skinColor),TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished);
return true;
}
catch (Exception ex)
@ -189,7 +189,7 @@ namespace TShockAPI.DB
{
database.Query(
"UPDATE tsCharacter SET Health = @0, MaxHealth = @1, Mana = @2, MaxMana = @3, Inventory = @4, spawnX = @6, spawnY = @7, hair = @8, hairDye = @9, hairColor = @10, pantsColor = @11, shirtColor = @12, underShirtColor = @13, shoeColor = @14, hideVisuals = @15, skinColor = @16, eyeColor = @17, questsCompleted = @18, skinVariant = @19, extraSlot = @20 WHERE Account = @5;",
playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), player.User.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisual), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0);
playerData.health, playerData.maxHealth, playerData.mana, playerData.maxMana, String.Join("~", playerData.inventory), player.Account.ID, player.TPlayer.SpawnX, player.TPlayer.SpawnY, player.TPlayer.hair, player.TPlayer.hairDye, TShock.Utils.EncodeColor(player.TPlayer.hairColor), TShock.Utils.EncodeColor(player.TPlayer.pantsColor), TShock.Utils.EncodeColor(player.TPlayer.shirtColor), TShock.Utils.EncodeColor(player.TPlayer.underShirtColor), TShock.Utils.EncodeColor(player.TPlayer.shoeColor), TShock.Utils.EncodeBoolArray(player.TPlayer.hideVisual), TShock.Utils.EncodeColor(player.TPlayer.skinColor), TShock.Utils.EncodeColor(player.TPlayer.eyeColor), player.TPlayer.anglerQuestsFinished, player.TPlayer.skinVariant, player.TPlayer.extraAccessory ? 1 : 0);
return true;
}
catch (Exception ex)
@ -235,17 +235,17 @@ namespace TShockAPI.DB
if (player.HasPermission(Permissions.bypassssc))
{
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.User.Name); // Debug code
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + player.Account.Name); // Debug code
return true;
}
if (!GetPlayerData(player, player.User.ID).exists)
if (!GetPlayerData(player, player.Account.ID).exists)
{
try
{
database.Query(
"INSERT INTO tsCharacter (Account, Health, MaxHealth, Mana, MaxMana, Inventory, extraSlot, spawnX, spawnY, skinVariant, hair, hairDye, hairColor, pantsColor, shirtColor, underShirtColor, shoeColor, hideVisuals, skinColor, eyeColor, questsCompleted) VALUES (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20);",
player.User.ID,
player.Account.ID,
playerData.health,
playerData.maxHealth,
playerData.mana,
@ -284,7 +284,7 @@ namespace TShockAPI.DB
playerData.mana,
playerData.maxMana,
String.Join("~", playerData.inventory),
player.User.ID,
player.Account.ID,
playerData.spawnX,
playerData.spawnX,
playerData.skinVariant,

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,51 +26,153 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB
{
/// <summary>
/// Represents the GroupManager, which is in charge of group management.
/// </summary>
public class GroupManager : IEnumerable<Group>
{
private IDbConnection database;
public readonly List<Group> groups = new List<Group>();
/// <summary>
/// Initializes a new instance of the <see cref="GroupManager"/> class with the specified database connection.
/// </summary>
/// <param name="db">The connection.</param>
public GroupManager(IDbConnection db)
{
database = db;
var table = new SqlTable("GroupList",
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) {Primary = true},
new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text)
);
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true },
new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder)new SqliteQueryCreator()
: new MysqlQueryCreator());
if (creator.EnsureTableStructure(table))
{
// Add default groups if they don't exist
AddDefaultGroup("guest", "",
string.Join(",", Permissions.canbuild, Permissions.canregister, Permissions.canlogin, Permissions.canpartychat,
Permissions.cantalkinthird, Permissions.canchat));
string.Join(",",
Permissions.canbuild,
Permissions.canregister,
Permissions.canlogin,
Permissions.canpartychat,
Permissions.cantalkinthird,
Permissions.canchat));
AddDefaultGroup("default", "guest",
string.Join(",", Permissions.warp, Permissions.canchangepassword, Permissions.canlogout));
string.Join(",",
Permissions.warp,
Permissions.canchangepassword,
Permissions.canlogout,
Permissions.summonboss,
Permissions.whisper,
Permissions.wormhole,
Permissions.canpaint));
AddDefaultGroup("newadmin", "default",
string.Join(",", Permissions.kick, Permissions.editspawn, Permissions.reservedslot));
AddDefaultGroup("vip", "default",
string.Join(",",
Permissions.reservedslot,
Permissions.renamenpc,
Permissions.startinvasion,
Permissions.summonboss,
Permissions.whisper,
Permissions.wormhole));
AddDefaultGroup("newadmin", "vip",
string.Join(",",
Permissions.kick,
Permissions.editspawn,
Permissions.reservedslot,
Permissions.annoy,
Permissions.checkaccountinfo,
Permissions.getpos,
Permissions.mute,
Permissions.rod,
Permissions.savessc,
Permissions.seeids,
"tshock.world.time.*"));
AddDefaultGroup("admin", "newadmin",
string.Join(",", Permissions.ban, Permissions.whitelist, "tshock.world.time.*", Permissions.spawnboss,
Permissions.spawnmob, Permissions.managewarp, Permissions.time, Permissions.tp, Permissions.slap,
Permissions.kill, Permissions.logs,
Permissions.immunetokick, Permissions.tpothers));
string.Join(",",
Permissions.ban,
Permissions.whitelist,
Permissions.spawnboss,
Permissions.spawnmob,
Permissions.managewarp,
Permissions.time,
Permissions.tp,
Permissions.slap,
Permissions.kill,
Permissions.logs,
Permissions.immunetokick,
Permissions.tpothers,
Permissions.advaccountinfo,
Permissions.broadcast,
Permissions.home,
Permissions.tpallothers,
Permissions.tpallow,
Permissions.tpnpc,
Permissions.tppos,
Permissions.tpsilent,
Permissions.userinfo));
AddDefaultGroup("trustedadmin", "admin",
string.Join(",", Permissions.maintenance, "tshock.cfg.*", "tshock.world.*", Permissions.butcher, Permissions.item, Permissions.give,
Permissions.heal, Permissions.immunetoban, Permissions.usebanneditem));
string.Join(",",
Permissions.maintenance,
"tshock.cfg.*",
"tshock.world.*",
Permissions.butcher,
Permissions.item,
Permissions.give,
Permissions.heal,
Permissions.immunetoban,
Permissions.usebanneditem,
Permissions.allowclientsideworldedit,
Permissions.buff,
Permissions.buffplayer,
Permissions.clear,
Permissions.clearangler,
Permissions.godmode,
Permissions.godmodeother,
Permissions.ignoredamagecap,
Permissions.ignorehp,
Permissions.ignorekilltiledetection,
Permissions.ignoreliquidsetdetection,
Permissions.ignoremp,
Permissions.ignorepaintdetection,
Permissions.ignoreplacetiledetection,
Permissions.ignoreprojectiledetection,
Permissions.ignorestackhackdetection,
Permissions.invade,
Permissions.startdd2,
Permissions.uploaddata,
Permissions.uploadothersdata));
AddDefaultGroup("vip", "default", string.Join(",", Permissions.reservedslot));
AddDefaultGroup("owner", "trustedadmin",
string.Join(",",
Permissions.su,
Permissions.allowdroppingbanneditems,
Permissions.antibuild,
Permissions.canusebannedprojectiles,
Permissions.canusebannedtiles,
Permissions.managegroup,
Permissions.manageitem,
Permissions.manageprojectile,
Permissions.manageregion,
Permissions.managetile,
Permissions.maxspawns,
Permissions.serverinfo,
Permissions.settempgroup,
Permissions.spawnrate,
Permissions.tpoverride,
Permissions.createdumps));
}
// Load Permissions from the DB
@ -85,7 +187,11 @@ namespace TShockAPI.DB
AddGroup(name, parent, permissions, Group.defaultChatColor);
}
/// <summary>
/// Determines whether the given group exists.
/// </summary>
/// <param name="group">The group.</param>
/// <returns><c>true</c> if it does; otherwise, <c>false</c>.</returns>
public bool GroupExists(string group)
{
if (group == "superadmin")
@ -99,11 +205,20 @@ namespace TShockAPI.DB
return GetEnumerator();
}
/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<Group> GetEnumerator()
{
return groups.GetEnumerator();
}
/// <summary>
/// Gets the group matching the specified name.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>The group.</returns>
public Group GetGroupByName(string name)
{
var ret = groups.Where(g => g.Name == name);
@ -139,8 +254,8 @@ namespace TShockAPI.DB
}
string query = (TShock.Config.StorageType.ToLower() == "sqlite")
? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);"
: "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);"
: "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
if (database.Query(query, name, parentname, permissions, chatcolor) == 1)
{
groups.Add(group);
@ -200,6 +315,114 @@ namespace TShockAPI.DB
group.Suffix = suffix;
}
/// <summary>
/// Renames the specified group.
/// </summary>
/// <param name="name">The group's name.</param>
/// <param name="newName">The new name.</param>
/// <returns>The result from the operation to be sent back to the user.</returns>
public String RenameGroup(string name, string newName)
{
if (!GroupExists(name))
{
throw new GroupNotExistException(name);
}
if (GroupExists(newName))
{
throw new GroupExistsException(newName);
}
using (var db = database.CloneEx())
{
db.Open();
using (var transaction = db.BeginTransaction())
{
try
{
using (var command = db.CreateCommand())
{
command.CommandText = "UPDATE GroupList SET GroupName = @0 WHERE GroupName = @1";
command.AddParameter("@0", newName);
command.AddParameter("@1", name);
command.ExecuteNonQuery();
}
var oldGroup = GetGroupByName(name);
var newGroup = new Group(newName, oldGroup.Parent, oldGroup.ChatColor, oldGroup.Permissions)
{
Prefix = oldGroup.Prefix,
Suffix = oldGroup.Suffix
};
groups.Remove(oldGroup);
groups.Add(newGroup);
// We need to check if the old group has been referenced as a parent and update those references accordingly
using (var command = db.CreateCommand())
{
command.CommandText = "UPDATE GroupList SET Parent = @0 WHERE Parent = @1";
command.AddParameter("@0", newName);
command.AddParameter("@1", name);
command.ExecuteNonQuery();
}
foreach (var group in groups.Where(g => g.Parent != null && g.Parent == oldGroup))
{
group.Parent = newGroup;
}
// Read the config file to prevent the possible loss of any unsaved changes
TShock.Config = ConfigFile.Read(FileTools.ConfigPath);
if (TShock.Config.DefaultGuestGroupName == oldGroup.Name)
{
TShock.Config.DefaultGuestGroupName = newGroup.Name;
Group.DefaultGroup = newGroup;
}
if (TShock.Config.DefaultRegistrationGroupName == oldGroup.Name)
{
TShock.Config.DefaultRegistrationGroupName = newGroup.Name;
}
TShock.Config.Write(FileTools.ConfigPath);
// We also need to check if any users belong to the old group and automatically apply changes
using (var command = db.CreateCommand())
{
command.CommandText = "UPDATE Users SET Usergroup = @0 WHERE Usergroup = @1";
command.AddParameter("@0", newName);
command.AddParameter("@1", name);
command.ExecuteNonQuery();
}
foreach (var player in TShock.Players.Where(p => p?.Group == oldGroup))
{
player.Group = newGroup;
}
transaction.Commit();
return $"Group \"{name}\" has been renamed to \"{newName}\".";
}
catch (Exception ex)
{
TShock.Log.Error($"An exception has occured during database transaction: {ex.Message}");
try
{
transaction.Rollback();
}
catch (Exception rollbackEx)
{
TShock.Log.Error($"An exception has occured during database rollback: {rollbackEx.Message}");
}
}
}
}
throw new GroupManagerException($"Failed to rename group \"{name}\".");
}
/// <summary>
/// Deletes the specified group.
/// </summary>
/// <param name="name">The group's name.</param>
/// <param name="exceptions">Whether exceptions will be thrown in case something goes wrong.</param>
/// <returns>The result from the operation to be sent back to the user.</returns>
public String DeleteGroup(String name, bool exceptions = false)
{
if (!GroupExists(name))
@ -211,21 +434,27 @@ namespace TShockAPI.DB
if (database.Query("DELETE FROM GroupList WHERE GroupName=@0", name) == 1)
{
groups.Remove(TShock.Utils.GetGroup(name));
groups.Remove(TShock.Groups.GetGroupByName(name));
return "Group " + name + " has been deleted successfully.";
}
else if (exceptions)
throw new GroupManagerException("Failed to delete group '" + name + ".'");
return "";
if (exceptions)
throw new GroupManagerException("Failed to delete group '" + name + ".'");
return "Failed to delete group '" + name + ".'";
}
/// <summary>
/// Enumerates the given permission list and adds permissions for the specified group accordingly.
/// </summary>
/// <param name="name">The group name.</param>
/// <param name="permissions">The permission list.</param>
/// <returns>The result from the operation to be sent back to the user.</returns>
public String AddPermissions(String name, List<String> permissions)
{
if (!GroupExists(name))
return "Error: Group doesn't exist.";
var group = TShock.Utils.GetGroup(name);
var group = TShock.Groups.GetGroupByName(name);
var oldperms = group.Permissions; // Store old permissions in case of error
permissions.ForEach(p => group.AddPermission(p));
@ -237,12 +466,18 @@ namespace TShockAPI.DB
return "";
}
/// <summary>
/// Enumerates the given permission list and removes valid permissions for the specified group accordingly.
/// </summary>
/// <param name="name">The group name.</param>
/// <param name="permissions">The permission list.</param>
/// <returns>The result from the operation to be sent back to the user.</returns>
public String DeletePermissions(String name, List<String> permissions)
{
if (!GroupExists(name))
return "Error: Group doesn't exist.";
var group = TShock.Utils.GetGroup(name);
var group = TShock.Groups.GetGroupByName(name);
var oldperms = group.Permissions; // Store old permissions in case of error
permissions.ForEach(p => group.RemovePermission(p));
@ -254,12 +489,15 @@ namespace TShockAPI.DB
return "";
}
/// <summary>
/// Enumerates the group list and loads permissions for each group appropriately.
/// </summary>
public void LoadPermisions()
{
try
{
List<Group> newGroups = new List<Group>(groups.Count);
Dictionary<string,string> newGroupParents = new Dictionary<string, string>(groups.Count);
Dictionary<string, string> newGroupParents = new Dictionary<string, string>(groups.Count);
using (var reader = database.QueryReader("SELECT * FROM GroupList"))
{
while (reader.Read())
@ -271,7 +509,8 @@ namespace TShockAPI.DB
continue;
}
newGroups.Add(new Group(groupName, null, reader.Get<string>("ChatColor"), reader.Get<string>("Commands")) {
newGroups.Add(new Group(groupName, null, reader.Get<string>("ChatColor"), reader.Get<string>("Commands"))
{
Prefix = reader.Get<string>("Prefix"),
Suffix = reader.Get<string>("Suffix"),
});
@ -360,32 +599,60 @@ namespace TShockAPI.DB
}
}
/// <summary>
/// Represents the base GroupManager exception.
/// </summary>
[Serializable]
public class GroupManagerException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupManagerException"/> with the specified message.
/// </summary>
/// <param name="message">The message.</param>
public GroupManagerException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GroupManagerException"/> with the specified message and inner exception.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="inner">The inner exception.</param>
public GroupManagerException(string message, Exception inner)
: base(message, inner)
{
}
}
/// <summary>
/// Represents the GroupExists exception.
/// This exception is thrown whenever an attempt to add an existing group into the database is made.
/// </summary>
[Serializable]
public class GroupExistsException : GroupManagerException
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupExistsException"/> with the specified group name.
/// </summary>
/// <param name="name">The group name.</param>
public GroupExistsException(string name)
: base("Group '" + name + "' already exists")
{
}
}
/// <summary>
/// Represents the GroupNotExist exception.
/// This exception is thrown whenever we try to access a group that does not exist.
/// </summary>
[Serializable]
public class GroupNotExistException : GroupManagerException
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupNotExistException"/> with the specified group name.
/// </summary>
/// <param name="name">The group name.</param>
public GroupNotExistException(string name)
: base("Group '" + name + "' does not exist")
{

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,45 +16,120 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using MySql.Data.MySqlClient;
using TShockAPI.Extensions;
namespace TShockAPI.DB
{
/// <summary>
/// Interface for various SQL related utilities.
/// </summary>
public interface IQueryBuilder
{
/// <summary>
/// Creates a table from a SqlTable object.
/// </summary>
/// <param name="table">The SqlTable to create the table from</param>
/// <returns>The sql query for the table creation.</returns>
string CreateTable(SqlTable table);
/// <summary>
/// Alter a table from source to destination
/// </summary>
/// <param name="from">Must have name and column names. Column types are not required</param>
/// <param name="to">Must have column names and column types.</param>
/// <returns>The SQL Query</returns>
string AlterTable(SqlTable from, SqlTable to);
/// <summary>
/// Converts the MySqlDbType enum to it's string representation.
/// </summary>
/// <param name="type">The MySqlDbType type</param>
/// <param name="length">The length of the datatype</param>
/// <returns>The string representation</returns>
string DbTypeToString(MySqlDbType type, int? length);
/// <summary>
/// A UPDATE Query
/// </summary>
/// <param name="table">The table to update</param>
/// <param name="values">The values to change</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres);
/// <summary>
/// A INSERT query
/// </summary>
/// <param name="table">The table to insert to</param>
/// <param name="values"></param>
/// <returns>The SQL Query</returns>
string InsertValues(string table, List<SqlValue> values);
/// <summary>
/// A SELECT query to get all columns
/// </summary>
/// <param name="table">The table to select from</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
string ReadColumn(string table, List<SqlValue> wheres);
/// <summary>
/// Deletes row(s).
/// </summary>
/// <param name="table">The table to delete the row from</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
string DeleteRow(string table, List<SqlValue> wheres);
/// <summary>
/// Renames the given table.
/// </summary>
/// <param name="from">Old name of the table</param>
/// <param name="to">New name of the table</param>
/// <returns>The sql query for renaming the table.</returns>
string RenameTable(string from, string to);
}
/// <summary>
/// Query Creator for Sqlite
/// </summary>
public class SqliteQueryCreator : GenericQueryCreator, IQueryBuilder
{
/// <summary>
/// Creates a table from a SqlTable object.
/// </summary>
/// <param name="table">The SqlTable to create the table from</param>
/// <returns>The sql query for the table creation.</returns>
public override string CreateTable(SqlTable table)
{
ValidateSqlColumnType(table.Columns);
var columns =
table.Columns.Select(
c =>
"'{0}' {1} {2} {3} {4}".SFormat(c.Name,
DbTypeToString(c.Type, c.Length),
"'{0}' {1} {2} {3} {4} {5}".SFormat(c.Name,
DbTypeToString(c.Type, c.Length),
c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "AUTOINCREMENT" : "",
c.NotNull ? "NOT NULL" : ""));
c.AutoIncrement ? "AUTOINCREMENT" : "",
c.NotNull ? "NOT NULL" : "",
c.DefaultCurrentTimestamp ? "DEFAULT CURRENT_TIMESTAMP" : ""));
var uniques = table.Columns.Where(c => c.Unique).Select(c => c.Name);
return "CREATE TABLE {0} ({1} {2})".SFormat(EscapeTableName(table.Name),
return "CREATE TABLE {0} ({1} {2})".SFormat(EscapeTableName(table.Name),
string.Join(", ", columns),
uniques.Count() > 0 ? ", UNIQUE({0})".SFormat(string.Join(", ", uniques)) : "");
}
/// <summary>
/// Renames the given table.
/// </summary>
/// <param name="from">Old name of the table</param>
/// <param name="to">New name of the table</param>
/// <returns>The sql query for renaming the table.</returns>
public override string RenameTable(string from, string to)
{
return "ALTER TABLE {0} RENAME TO {1}".SFormat(from, to);
@ -72,9 +147,16 @@ namespace TShockAPI.DB
{ MySqlDbType.Double, "REAL" },
{ MySqlDbType.Int32, "INTEGER" },
{ MySqlDbType.Blob, "BLOB" },
{ MySqlDbType.Int64, "BIGINT"},
{ MySqlDbType.Int64, "BIGINT"},
{ MySqlDbType.DateTime, "DATETIME"},
};
/// <summary>
/// Converts the MySqlDbType enum to it's string representation.
/// </summary>
/// <param name="type">The MySqlDbType type</param>
/// <param name="length">The length of the datatype</param>
/// <returns>The string representation</returns>
public string DbTypeToString(MySqlDbType type, int? length)
{
string ret;
@ -83,21 +165,38 @@ namespace TShockAPI.DB
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type));
}
/// <summary>
/// Escapes the table name
/// </summary>
/// <param name="table">The name of the table to be escaped</param>
/// <returns></returns>
protected override string EscapeTableName(string table)
{
return table.SFormat("'{0}'", table);
}
}
/// <summary>
/// Query Creator for MySQL
/// </summary>
public class MysqlQueryCreator : GenericQueryCreator, IQueryBuilder
{
/// <summary>
/// Creates a table from a SqlTable object.
/// </summary>
/// <param name="table">The SqlTable to create the table from</param>
/// <returns>The sql query for the table creation.</returns>
public override string CreateTable(SqlTable table)
{
ValidateSqlColumnType(table.Columns);
var columns =
table.Columns.Select(
c =>
"{0} {1} {2} {3} {4}".SFormat(c.Name, DbTypeToString(c.Type, c.Length), c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "AUTO_INCREMENT" : "", c.NotNull ? "NOT NULL" : ""));
"{0} {1} {2} {3} {4} {5}".SFormat(c.Name, DbTypeToString(c.Type, c.Length),
c.Primary ? "PRIMARY KEY" : "",
c.AutoIncrement ? "AUTO_INCREMENT" : "",
c.NotNull ? "NOT NULL" : "",
c.DefaultCurrentTimestamp ? "DEFAULT CURRENT_TIMESTAMP" : ""));
var uniques = table.Columns.Where(c => c.Unique).Select(c => c.Name);
return "CREATE TABLE {0} ({1} {2})".SFormat(EscapeTableName(table.Name), string.Join(", ", columns),
uniques.Count() > 0
@ -105,6 +204,12 @@ namespace TShockAPI.DB
: "");
}
/// <summary>
/// Renames the given table.
/// </summary>
/// <param name="from">Old name of the table</param>
/// <param name="to">New name of the table</param>
/// <returns>The sql query for renaming the table.</returns>
public override string RenameTable(string from, string to)
{
return "RENAME TABLE {0} TO {1}".SFormat(from, to);
@ -121,9 +226,16 @@ namespace TShockAPI.DB
{ MySqlDbType.Float, "FLOAT" },
{ MySqlDbType.Double, "DOUBLE" },
{ MySqlDbType.Int32, "INT" },
{ MySqlDbType.Int64, "BIGINT"},
{ MySqlDbType.Int64, "BIGINT"},
{ MySqlDbType.DateTime, "DATETIME"},
};
/// <summary>
/// Converts the MySqlDbType enum to it's string representation.
/// </summary>
/// <param name="type">The MySqlDbType type</param>
/// <param name="length">The length of the datatype</param>
/// <returns>The string representation</returns>
public string DbTypeToString(MySqlDbType type, int? length)
{
string ret;
@ -132,17 +244,44 @@ namespace TShockAPI.DB
throw new NotImplementedException(Enum.GetName(typeof(MySqlDbType), type));
}
/// <summary>
/// Escapes the table name
/// </summary>
/// <param name="table">The name of the table to be escaped</param>
/// <returns></returns>
protected override string EscapeTableName(string table)
{
return table.SFormat("`{0}`", table);
}
}
/// <summary>
/// A Generic Query Creator (abstract)
/// </summary>
public abstract class GenericQueryCreator
{
protected static Random rand = new Random();
/// <summary>
/// Escapes the table name
/// </summary>
/// <param name="table">The name of the table to be escaped</param>
/// <returns></returns>
protected abstract string EscapeTableName(string table);
/// <summary>
/// Creates a table from a SqlTable object.
/// </summary>
/// <param name="table">The SqlTable to create the table from</param>
/// <returns>The sql query for the table creation.</returns>
public abstract string CreateTable(SqlTable table);
/// <summary>
/// Renames the given table.
/// </summary>
/// <param name="from">Old name of the table</param>
/// <param name="to">New name of the table</param>
/// <returns>The sql query for renaming the table.</returns>
public abstract string RenameTable(string from, string to);
/// <summary>
@ -150,18 +289,9 @@ namespace TShockAPI.DB
/// </summary>
/// <param name="from">Must have name and column names. Column types are not required</param>
/// <param name="to">Must have column names and column types.</param>
/// <returns></returns>
/// <returns>The SQL Query</returns>
public string AlterTable(SqlTable from, SqlTable to)
{
/*
* Any example outpuf from this looks like:-
ALTER TABLE "main"."Bans" RENAME TO "oXHFcGcd04oXHFcGcd04_Bans"
CREATE TABLE "main"."Bans" ("IP" TEXT PRIMARY KEY ,"Name" TEXT)
INSERT INTO "main"."Bans" SELECT "IP","Name" FROM "main"."oXHFcGcd04oXHFcGcd04_Bans"
DROP TABLE "main"."oXHFcGcd04oXHFcGcd04_Bans"
*
* Twitchy - Oh. I get it!
*/
var rstr = rand.NextString(20);
var escapedTable = EscapeTableName(from.Name);
var tmpTable = EscapeTableName("{0}_{1}".SFormat(rstr, from.Name));
@ -175,11 +305,41 @@ namespace TShockAPI.DB
return "{0}; {1}; {2}; {3};".SFormat(alter, create, insert, drop);
}
/// <summary>
/// Check for errors in the columns.
/// </summary>
/// <param name="columns"></param>
/// <exception cref="SqlColumnException"></exception>
public void ValidateSqlColumnType(List<SqlColumn> columns)
{
columns.ForEach(x =>
{
if (x.DefaultCurrentTimestamp && x.Type != MySqlDbType.DateTime)
{
throw new SqlColumnException("Can't set to true SqlColumn.DefaultCurrentTimestamp " +
"when the MySqlDbType is not DateTime");
}
});
}
/// <summary>
/// Deletes row(s).
/// </summary>
/// <param name="table">The table to delete the row from</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
public string DeleteRow(string table, List<SqlValue> wheres)
{
return "DELETE FROM {0} {1}".SFormat(EscapeTableName(table), BuildWhere(wheres));
}
/// <summary>
/// A UPDATE Query
/// </summary>
/// <param name="table">The table to update</param>
/// <param name="values">The values to change</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
public string UpdateValue(string table, List<SqlValue> values, List<SqlValue> wheres)
{
if (0 == values.Count)
@ -188,11 +348,23 @@ namespace TShockAPI.DB
return "UPDATE {0} SET {1} {2}".SFormat(EscapeTableName(table), string.Join(", ", values.Select(v => v.Name + " = " + v.Value)), BuildWhere(wheres));
}
/// <summary>
/// A SELECT query to get all columns
/// </summary>
/// <param name="table">The table to select from</param>
/// <param name="wheres"></param>
/// <returns>The SQL query</returns>
public string ReadColumn(string table, List<SqlValue> wheres)
{
return "SELECT * FROM {0} {1}".SFormat(EscapeTableName(table), BuildWhere(wheres));
}
/// <summary>
/// A INSERT query
/// </summary>
/// <param name="table">The table to insert to</param>
/// <param name="values"></param>
/// <returns>The SQL Query</returns>
public string InsertValues(string table, List<SqlValue> values)
{
var sbnames = new StringBuilder();
@ -214,6 +386,11 @@ namespace TShockAPI.DB
return "INSERT INTO {0} ({1}) VALUES ({2})".SFormat(EscapeTableName(table), sbnames, sbvalues);
}
/// <summary>
/// Builds the SQL WHERE clause
/// </summary>
/// <param name="wheres"></param>
/// <returns></returns>
protected static string BuildWhere(List<SqlValue> wheres)
{
if (0 == wheres.Count)

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
using MySql.Data.MySqlClient;
using TShockAPI.Hooks;
namespace TShockAPI.DB
{
@ -200,6 +201,10 @@ namespace TShockAPI.DB
if (ply.HasPermission(Permissions.usebanneditem))
return true;
PermissionHookResult hookResult = PlayerHooks.OnPlayerItembanPermission(ply, this);
if (hookResult != PermissionHookResult.Unhandled)
return hookResult == PermissionHookResult.Granted;
var cur = ply.Group;
var traversed = new List<Group>();
while (cur != null)

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
using MySql.Data.MySqlClient;
using TShockAPI.Hooks;
namespace TShockAPI.DB
{
@ -205,6 +206,10 @@ namespace TShockAPI.DB
if (ply.HasPermission(Permissions.canusebannedprojectiles))
return true;
PermissionHookResult hookResult = PlayerHooks.OnPlayerProjbanPermission(ply, this);
if (hookResult != PermissionHookResult.Unhandled)
return hookResult == PermissionHookResult.Granted;
var cur = ply.Group;
var traversed = new List<Group>();
while (cur != null)

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,6 +26,9 @@ using Microsoft.Xna.Framework;
namespace TShockAPI.DB
{
/// <summary>
/// Represents the Region database manager.
/// </summary>
public class RegionManager
{
/// <summary>
@ -401,7 +404,41 @@ namespace TShockAPI.DB
}
return false;
}
/// <summary>
/// Renames a region
/// </summary>
/// <param name="oldName">Name of the region to rename</param>
/// <param name="newName">New name of the region</param>
/// <returns>true if renamed successfully, false otherwise</returns>
public bool RenameRegion(string oldName, string newName)
{
Region region = null;
string worldID = Main.worldID.ToString();
bool result = false;
try
{
int q = database.Query("UPDATE Regions SET RegionName = @0 WHERE RegionName = @1 AND WorldID = @2",
newName, oldName, worldID);
if (q > 0)
{
region = Regions.First(r => r.Name == oldName && r.WorldID == worldID);
region.Name = newName;
Hooks.RegionHooks.OnRegionRenamed(region, oldName, newName);
result = true;
}
}
catch (Exception ex)
{
TShock.Log.Error(ex.ToString());
}
return result;
}
/// <summary>
/// Removes an allowed user from a region
/// </summary>
@ -413,7 +450,7 @@ namespace TShockAPI.DB
Region r = GetRegionByName(regionName);
if (r != null)
{
if (!r.RemoveID(TShock.Users.GetUserID(userName)))
if (!r.RemoveID(TShock.UserAccounts.GetUserAccountID(userName)))
{
return false;
}
@ -445,7 +482,7 @@ namespace TShockAPI.DB
mergedIDs = reader.Get<string>("UserIds");
}
string userIdToAdd = Convert.ToString(TShock.Users.GetUserID(userName));
string userIdToAdd = Convert.ToString(TShock.UserAccounts.GetUserAccountID(userName));
string[] ids = mergedIDs.Split(',');
// Is the user already allowed to the region?
if (ids.Contains(userIdToAdd))
@ -754,7 +791,7 @@ namespace TShockAPI.DB
return false;
}
return ply.HasPermission(Permissions.editregion) || AllowedIDs.Contains(ply.User.ID) || AllowedGroups.Contains(ply.Group.Name) || Owner == ply.User.Name;
return ply.HasPermission(Permissions.editregion) || AllowedIDs.Contains(ply.Account.ID) || AllowedGroups.Contains(ply.Group.Name) || Owner == ply.Account.Name;
}
/// <summary>

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using MySql.Data.MySqlClient;
using System;
namespace TShockAPI.DB
{
@ -28,11 +29,30 @@ namespace TShockAPI.DB
//Optional
/// <summary>
/// Sets/Gets if it's unique
/// </summary>
public bool Unique { get; set; }
/// <summary>
/// Sets/Gets if it's primary key
/// </summary>
public bool Primary { get; set; }
/// <summary>
/// Sets/Gets if it autoincrements
/// </summary>
public bool AutoIncrement { get; set; }
/// <summary>
/// Sets/Gets if it can be or not null
/// </summary>
public bool NotNull { get; set; }
/// <summary>
/// Sets the default value
/// </summary>
public string DefaultValue { get; set; }
/// <summary>
/// Use on DateTime only, if true, sets the default value to the current date when creating the row.
/// </summary>
public bool DefaultCurrentTimestamp { get; set; }
/// <summary>
/// Length of the data type, null = default
@ -51,4 +71,19 @@ namespace TShockAPI.DB
Length = length;
}
}
}
/// <summary>
/// Used when a SqlColumn has validation errors.
/// </summary>
[Serializable]
public class SqlColumnException : Exception
{
/// <summary>
/// Creates a new SqlColumnException with the given message.
/// </summary>
/// <param name="message"></param>
public SqlColumnException(string message) : base(message)
{
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
using MySql.Data.MySqlClient;
using TShockAPI.Hooks;
namespace TShockAPI.DB
{
@ -205,6 +206,10 @@ namespace TShockAPI.DB
if (ply.HasPermission(Permissions.canusebannedtiles))
return true;
PermissionHookResult hookResult = PlayerHooks.OnPlayerTilebanPermission(ply, this);
if (hookResult != PermissionHookResult.Unhandled)
return hookResult == PermissionHookResult.Granted;
var cur = ply.Group;
var traversed = new List<Group>();
while (cur != null)

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -28,16 +28,16 @@ using System.Security.Cryptography;
namespace TShockAPI.DB
{
/// <summary>UserManager - Methods for dealing with database users and other user functionality within TShock.</summary>
public class UserManager
/// <summary>UserAccountManager - Methods for dealing with database user accounts and other related functionality within TShock.</summary>
public class UserAccountManager
{
/// <summary>database - The database object to use for connections.</summary>
private IDbConnection _database;
/// <summary>Creates a UserManager object. During instantiation, this method will verify the table structure against the format below.</summary>
/// <summary>Creates a UserAccountManager object. During instantiation, this method will verify the table structure against the format below.</summary>
/// <param name="db">The database to connect to.</param>
/// <returns>A UserManager object.</returns>
public UserManager(IDbConnection db)
/// <returns>A UserAccountManager object.</returns>
public UserAccountManager(IDbConnection db)
{
_database = db;
@ -59,145 +59,149 @@ namespace TShockAPI.DB
}
/// <summary>
/// Adds a given username to the database
/// Adds the given user account to the database
/// </summary>
/// <param name="user">User user</param>
public void AddUser(User user)
/// <param name="account">The user account to be added</param>
public void AddUserAccount(UserAccount account)
{
if (!TShock.Groups.GroupExists(user.Group))
throw new GroupNotExistsException(user.Group);
if (!TShock.Groups.GroupExists(account.Group))
throw new GroupNotExistsException(account.Group);
int ret;
try
{
ret = _database.Query("INSERT INTO Users (Username, Password, UUID, UserGroup, Registered) VALUES (@0, @1, @2, @3, @4);", user.Name,
user.Password, user.UUID, user.Group, DateTime.UtcNow.ToString("s"));
ret = _database.Query("INSERT INTO Users (Username, Password, UUID, UserGroup, Registered) VALUES (@0, @1, @2, @3, @4);", account.Name,
account.Password, account.UUID, account.Group, DateTime.UtcNow.ToString("s"));
}
catch (Exception ex)
{
// Detect duplicate user using a regexp as Sqlite doesn't have well structured exceptions
if (Regex.IsMatch(ex.Message, "Username.*not unique"))
throw new UserExistsException(user.Name);
throw new UserManagerException("AddUser SQL returned an error (" + ex.Message + ")", ex);
if (Regex.IsMatch(ex.Message, "Username.*not unique|UNIQUE constraint failed: Users\\.Username"))
throw new UserAccountExistsException(account.Name);
throw new UserAccountManagerException("AddUser SQL returned an error (" + ex.Message + ")", ex);
}
if (1 > ret)
throw new UserExistsException(user.Name);
throw new UserAccountExistsException(account.Name);
Hooks.AccountHooks.OnAccountCreate(user);
Hooks.AccountHooks.OnAccountCreate(account);
}
/// <summary>
/// Removes a given username from the database
/// Removes all user accounts from the database whose usernames match the given user account
/// </summary>
/// <param name="user">User user</param>
public void RemoveUser(User user)
/// <param name="account">The user account</param>
public void RemoveUserAccount(UserAccount account)
{
try
{
var tempuser = GetUser(user);
int affected = _database.Query("DELETE FROM Users WHERE Username=@0", user.Name);
// Logout any player logged in as the account to be removed
TShock.Players.Where(p => p?.IsLoggedIn == true && p.Account.Name == account.Name).ForEach(p => p.Logout());
UserAccount tempuser = GetUserAccount(account);
int affected = _database.Query("DELETE FROM Users WHERE Username=@0", account.Name);
if (affected < 1)
throw new UserNotExistException(user.Name);
throw new UserAccountNotExistException(account.Name);
Hooks.AccountHooks.OnAccountDelete(tempuser);
}
catch (Exception ex)
{
throw new UserManagerException("RemoveUser SQL returned an error", ex);
throw new UserAccountManagerException("RemoveUser SQL returned an error", ex);
}
}
/// <summary>
/// Sets the Hashed Password for a given username
/// </summary>
/// <param name="user">User user</param>
/// <param name="password">string password</param>
public void SetUserPassword(User user, string password)
/// <param name="account">The user account</param>
/// <param name="password">The user account password to be set</param>
public void SetUserAccountPassword(UserAccount account, string password)
{
try
{
user.CreateBCryptHash(password);
account.CreateBCryptHash(password);
if (
_database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", user.Password,
user.Name) == 0)
throw new UserNotExistException(user.Name);
_database.Query("UPDATE Users SET Password = @0 WHERE Username = @1;", account.Password,
account.Name) == 0)
throw new UserAccountNotExistException(account.Name);
}
catch (Exception ex)
{
throw new UserManagerException("SetUserPassword SQL returned an error", ex);
throw new UserAccountManagerException("SetUserPassword SQL returned an error", ex);
}
}
/// <summary>
/// Sets the UUID for a given username
/// </summary>
/// <param name="user">User user</param>
/// <param name="uuid">string uuid</param>
public void SetUserUUID(User user, string uuid)
/// <param name="account">The user account</param>
/// <param name="uuid">The user account uuid to be set</param>
public void SetUserAccountUUID(UserAccount account, string uuid)
{
try
{
if (
_database.Query("UPDATE Users SET UUID = @0 WHERE Username = @1;", uuid,
user.Name) == 0)
throw new UserNotExistException(user.Name);
account.Name) == 0)
throw new UserAccountNotExistException(account.Name);
}
catch (Exception ex)
{
throw new UserManagerException("SetUserUUID SQL returned an error", ex);
throw new UserAccountManagerException("SetUserUUID SQL returned an error", ex);
}
}
/// <summary>
/// Sets the group for a given username
/// </summary>
/// <param name="user">User user</param>
/// <param name="group">string group</param>
public void SetUserGroup(User user, string group)
/// <param name="account">The user account</param>
/// <param name="group">The user account group to be set</param>
public void SetUserGroup(UserAccount account, string group)
{
Group grp = TShock.Groups.GetGroupByName(group);
if (null == grp)
throw new GroupNotExistsException(group);
if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, user.Name) == 0)
throw new UserNotExistException(user.Name);
if (_database.Query("UPDATE Users SET UserGroup = @0 WHERE Username = @1;", group, account.Name) == 0)
throw new UserAccountNotExistException(account.Name);
try
{
// Update player group reference for any logged in player
foreach (var player in TShock.Players.Where(p => p != null && p.User != null && p.User.Name == user.Name))
foreach (var player in TShock.Players.Where(p => p != null && p.Account != null && p.Account.Name == account.Name))
{
player.Group = grp;
}
}
catch (Exception ex)
{
throw new UserManagerException("SetUserGroup SQL returned an error", ex);
throw new UserAccountManagerException("SetUserGroup SQL returned an error", ex);
}
}
/// <summary>Updates the last accessed time for a database user to the current time.</summary>
/// <param name="user">The user object to modify.</param>
public void UpdateLogin(User user)
/// <summary>Updates the last accessed time for a database user account to the current time.</summary>
/// <param name="account">The user account object to modify.</param>
public void UpdateLogin(UserAccount account)
{
try
{
if (_database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.UtcNow.ToString("s"), user.KnownIps, user.Name) == 0)
throw new UserNotExistException(user.Name);
if (_database.Query("UPDATE Users SET LastAccessed = @0, KnownIps = @1 WHERE Username = @2;", DateTime.UtcNow.ToString("s"), account.KnownIps, account.Name) == 0)
throw new UserAccountNotExistException(account.Name);
}
catch (Exception ex)
{
throw new UserManagerException("UpdateLogin SQL returned an error", ex);
throw new UserAccountManagerException("UpdateLogin SQL returned an error", ex);
}
}
/// <summary>Gets the database ID of a given user object from the database.</summary>
/// <param name="username">The username of the user to query for.</param>
/// <returns>The user's ID</returns>
public int GetUserID(string username)
/// <summary>Gets the database ID of a given user account object from the database.</summary>
/// <param name="username">The username of the user account to query for.</param>
/// <returns>The user account ID</returns>
public int GetUserAccountID(string username)
{
try
{
@ -216,55 +220,55 @@ namespace TShockAPI.DB
return -1;
}
/// <summary>Gets a user object by name.</summary>
/// <summary>Gets a user account object by name.</summary>
/// <param name="name">The user's name.</param>
/// <returns>The user object returned from the search.</returns>
public User GetUserByName(string name)
/// <returns>The user account object returned from the search.</returns>
public UserAccount GetUserAccountByName(string name)
{
try
{
return GetUser(new User {Name = name});
return GetUserAccount(new UserAccount {Name = name});
}
catch (UserManagerException)
catch (UserAccountManagerException)
{
return null;
}
}
/// <summary>Gets a user object by their user ID.</summary>
/// <summary>Gets a user account object by their user account ID.</summary>
/// <param name="id">The user's ID.</param>
/// <returns>The user object returned from the search.</returns>
public User GetUserByID(int id)
/// <returns>The user account object returned from the search.</returns>
public UserAccount GetUserAccountByID(int id)
{
try
{
return GetUser(new User {ID = id});
return GetUserAccount(new UserAccount {ID = id});
}
catch (UserManagerException)
catch (UserAccountManagerException)
{
return null;
}
}
/// <summary>Gets a user object by a user object.</summary>
/// <param name="user">The user object to search by.</param>
/// <summary>Gets a user account object by a user account object.</summary>
/// <param name="account">The user account object to search by.</param>
/// <returns>The user object that is returned from the search.</returns>
public User GetUser(User user)
public UserAccount GetUserAccount(UserAccount account)
{
bool multiple = false;
string query;
string type;
object arg;
if (0 != user.ID)
if (account.ID != 0)
{
query = "SELECT * FROM Users WHERE ID=@0";
arg = user.ID;
arg = account.ID;
type = "id";
}
else
{
query = "SELECT * FROM Users WHERE Username=@0";
arg = user.Name;
arg = account.Name;
type = "name";
}
@ -274,38 +278,38 @@ namespace TShockAPI.DB
{
if (result.Read())
{
user = LoadUserFromResult(user, result);
account = LoadUserAccountFromResult(account, result);
// Check for multiple matches
if (!result.Read())
return user;
return account;
multiple = true;
}
}
}
catch (Exception ex)
{
throw new UserManagerException("GetUser SQL returned an error (" + ex.Message + ")", ex);
throw new UserAccountManagerException("GetUser SQL returned an error (" + ex.Message + ")", ex);
}
if (multiple)
throw new UserManagerException(String.Format("Multiple users found for {0} '{1}'", type, arg));
throw new UserAccountManagerException(String.Format("Multiple user accounts found for {0} '{1}'", type, arg));
throw new UserNotExistException(user.Name);
throw new UserAccountNotExistException(account.Name);
}
/// <summary>Gets all users from the database.</summary>
/// <returns>The users from the database.</returns>
public List<User> GetUsers()
/// <summary>Gets all the user accounts from the database.</summary>
/// <returns>The user accounts from the database.</returns>
public List<UserAccount> GetUserAccounts()
{
try
{
List<User> users = new List<User>();
List<UserAccount> accounts = new List<UserAccount>();
using (var reader = _database.QueryReader("SELECT * FROM Users"))
{
while (reader.Read())
{
users.Add(LoadUserFromResult(new User(), reader));
accounts.Add(LoadUserAccountFromResult(new UserAccount(), reader));
}
return users;
return accounts;
}
}
catch (Exception ex)
@ -316,26 +320,26 @@ namespace TShockAPI.DB
}
/// <summary>
/// Gets all users from the database with a username that starts with or contains <see cref="username"/>
/// Gets all user accounts from the database with a username that starts with or contains <see cref="username"/>
/// </summary>
/// <param name="username">Rough username search. "n" will match "n", "na", "nam", "name", etc</param>
/// <param name="notAtStart">If <see cref="username"/> is not the first part of the username. If true then "name" would match "name", "username", "wordsnamewords", etc</param>
/// <returns>Matching users or null if exception is thrown</returns>
public List<User> GetUsersByName(string username, bool notAtStart = false)
public List<UserAccount> GetUserAccountsByName(string username, bool notAtStart = false)
{
try
{
List<User> users = new List<User>();
List<UserAccount> accounts = new List<UserAccount>();
string search = notAtStart ? string.Format("%{0}%", username) : string.Format("{0}%", username);
using (var reader = _database.QueryReader("SELECT * FROM Users WHERE Username LIKE @0",
search))
{
while (reader.Read())
{
users.Add(LoadUserFromResult(new User(), reader));
accounts.Add(LoadUserAccountFromResult(new UserAccount(), reader));
}
}
return users;
return accounts;
}
catch (Exception ex)
{
@ -344,61 +348,61 @@ namespace TShockAPI.DB
return null;
}
/// <summary>Fills out the fields of a User object with the results from a QueryResult object.</summary>
/// <param name="user">The user to add data to.</param>
/// <summary>Fills out the fields of a User account object with the results from a QueryResult object.</summary>
/// <param name="account">The user account to add data to.</param>
/// <param name="result">The QueryResult object to add data from.</param>
/// <returns>The 'filled out' user object.</returns>
private User LoadUserFromResult(User user, QueryResult result)
private UserAccount LoadUserAccountFromResult(UserAccount account, QueryResult result)
{
user.ID = result.Get<int>("ID");
user.Group = result.Get<string>("Usergroup");
user.Password = result.Get<string>("Password");
user.UUID = result.Get<string>("UUID");
user.Name = result.Get<string>("Username");
user.Registered = result.Get<string>("Registered");
user.LastAccessed = result.Get<string>("LastAccessed");
user.KnownIps = result.Get<string>("KnownIps");
return user;
account.ID = result.Get<int>("ID");
account.Group = result.Get<string>("Usergroup");
account.Password = result.Get<string>("Password");
account.UUID = result.Get<string>("UUID");
account.Name = result.Get<string>("Username");
account.Registered = result.Get<string>("Registered");
account.LastAccessed = result.Get<string>("LastAccessed");
account.KnownIps = result.Get<string>("KnownIps");
return account;
}
}
/// <summary>A database user.</summary>
public class User
/// <summary>A database user account.</summary>
public class UserAccount : IEquatable<UserAccount>
{
/// <summary>The database ID of the user.</summary>
/// <summary>The database ID of the user account.</summary>
public int ID { get; set; }
/// <summary>The user's name.</summary>
public string Name { get; set; }
/// <summary>The hashed password for the user.</summary>
/// <summary>The hashed password for the user account.</summary>
public string Password { get; internal set; }
/// <summary>The user's saved Univerally Unique Identifier token.</summary>
public string UUID { get; set; }
/// <summary>The group object that the user is a part of.</summary>
/// <summary>The group object that the user account is a part of.</summary>
public string Group { get; set; }
/// <summary>The unix epoch corresponding to the registration date of the user.</summary>
/// <summary>The unix epoch corresponding to the registration date of the user account.</summary>
public string Registered { get; set; }
/// <summary>The unix epoch corresponding to the last access date of the user.</summary>
/// <summary>The unix epoch corresponding to the last access date of the user account.</summary>
public string LastAccessed { get; set; }
/// <summary>A JSON serialized list of known IP addresses for a user.</summary>
/// <summary>A JSON serialized list of known IP addresses for a user account.</summary>
public string KnownIps { get; set; }
/// <summary>Constructor for the user object, assuming you define everything yourself.</summary>
/// <summary>Constructor for the user account object, assuming you define everything yourself.</summary>
/// <param name="name">The user's name.</param>
/// <param name="pass">The user's password hash.</param>
/// <param name="uuid">The user's UUID.</param>
/// <param name="group">The user's group name.</param>
/// <param name="registered">The unix epoch for the registration date.</param>
/// <param name="last">The unix epoch for the last access date.</param>
/// <param name="known">The known IPs for the user, serialized as a JSON object</param>
/// <returns>A completed user object.</returns>
public User(string name, string pass, string uuid, string group, string registered, string last, string known)
/// <param name="known">The known IPs for the user account, serialized as a JSON object</param>
/// <returns>A completed user account object.</returns>
public UserAccount(string name, string pass, string uuid, string group, string registered, string last, string known)
{
Name = name;
Password = pass;
@ -409,9 +413,9 @@ namespace TShockAPI.DB
KnownIps = known;
}
/// <summary>Default constructor for a user object; holds no data.</summary>
/// <returns>A user object.</returns>
public User()
/// <summary>Default constructor for a user account object; holds no data.</summary>
/// <returns>A user account object.</returns>
public UserAccount()
{
Name = "";
Password = "";
@ -428,7 +432,7 @@ namespace TShockAPI.DB
/// If the password is stored using BCrypt, it will be re-saved if the work factor in the config
/// is greater than the existing work factor with the new work factor.
/// </summary>
/// <param name="password">The password to check against the user object.</param>
/// <param name="password">The password to check against the user account object.</param>
/// <returns>bool true, if the password matched, or false, if it didn't.</returns>
public bool VerifyPassword(string password)
{
@ -459,7 +463,7 @@ namespace TShockAPI.DB
}
/// <summary>Upgrades a password to BCrypt, from an insecure hashing algorithm.</summary>
/// <param name="password">The raw user password (unhashed) to upgrade</param>
/// <param name="password">The raw user account password (unhashed) to upgrade</param>
protected void UpgradePasswordToBCrypt(string password)
{
// Save the old password, in the event that we have to revert changes.
@ -467,9 +471,9 @@ namespace TShockAPI.DB
try
{
TShock.Users.SetUserPassword(this, password);
TShock.UserAccounts.SetUserAccountPassword(this, password);
}
catch (UserManagerException e)
catch (UserAccountManagerException e)
{
TShock.Log.ConsoleError(e.ToString());
Password = oldpassword; // Revert changes
@ -477,7 +481,7 @@ namespace TShockAPI.DB
}
/// <summary>Upgrades a password to the highest work factor available in the config.</summary>
/// <param name="password">The raw user password (unhashed) to upgrade</param>
/// <param name="password">The raw user account password (unhashed) to upgrade</param>
protected void UpgradePasswordWorkFactor(string password)
{
// If the destination work factor is not greater, we won't upgrade it or re-hash it
@ -496,16 +500,16 @@ namespace TShockAPI.DB
{
try
{
TShock.Users.SetUserPassword(this, password);
TShock.UserAccounts.SetUserAccountPassword(this, password);
}
catch (UserManagerException e)
catch (UserAccountManagerException e)
{
TShock.Log.ConsoleError(e.ToString());
}
}
}
/// <summary>Creates a BCrypt hash for a user and stores it in this object.</summary>
/// <summary>Creates a BCrypt hash for a user account and stores it in this object.</summary>
/// <param name="password">The plain text password to hash</param>
public void CreateBCryptHash(string password)
{
@ -524,7 +528,7 @@ namespace TShockAPI.DB
}
}
/// <summary>Creates a BCrypt hash for a user and stores it in this object.</summary>
/// <summary>Creates a BCrypt hash for a user account and stores it in this object.</summary>
/// <param name="password">The plain text password to hash</param>
/// <param name="workFactor">The work factor to use in generating the password hash</param>
public void CreateBCryptHash(string password, int workFactor)
@ -581,59 +585,121 @@ namespace TShockAPI.DB
return HashPassword(Encoding.UTF8.GetBytes(password));
}
#region IEquatable
/// <summary>Indicates whether the current <see cref="UserAccount"/> is equal to another <see cref="UserAccount"/>.</summary>
/// <returns>true if the <see cref="UserAccount"/> is equal to the <paramref name="other" /> parameter; otherwise, false.</returns>
/// <param name="other">An <see cref="UserAccount"/> to compare with this <see cref="UserAccount"/>.</param>
public bool Equals(UserAccount other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return ID == other.ID && string.Equals(Name, other.Name);
}
/// <summary>Indicates whether the current <see cref="UserAccount"/> is equal to another object.</summary>
/// <returns>true if the <see cref="UserAccount"/> is equal to the <paramref name="obj" /> parameter; otherwise, false.</returns>
/// <param name="obj">An <see cref="object"/> to compare with this <see cref="UserAccount"/>.</param>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UserAccount)obj);
}
/// <summary>Serves as the hash function. </summary>
/// <returns>A hash code for the current <see cref="UserAccount"/>.</returns>
public override int GetHashCode()
{
unchecked
{
return (ID * 397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
/// <summary>
/// Compares equality of two <see cref="UserAccount"/> objects.
/// </summary>
/// <param name="left">Left hand of the comparison.</param>
/// <param name="right">Right hand of the comparison.</param>
/// <returns>true if the <see cref="UserAccount"/> objects are equal; otherwise, false.</returns>
public static bool operator ==(UserAccount left, UserAccount right)
{
return Equals(left, right);
}
/// <summary>
/// Compares equality of two <see cref="UserAccount"/> objects.
/// </summary>
/// <param name="left">Left hand of the comparison.</param>
/// <param name="right">Right hand of the comparison.</param>
/// <returns>true if the <see cref="UserAccount"/> objects aren't equal; otherwise, false.</returns>
public static bool operator !=(UserAccount left, UserAccount right)
{
return !Equals(left, right);
}
#endregion
/// <summary>
/// Converts the UserAccount to it's string representation
/// </summary>
/// <returns>Returns the UserAccount string representation</returns>
public override string ToString() => Name;
}
/// <summary>UserManagerException - An exception generated by the user manager.</summary>
/// <summary>UserAccountManagerException - An exception generated by the user account manager.</summary>
[Serializable]
public class UserManagerException : Exception
public class UserAccountManagerException : Exception
{
/// <summary>Creates a new UserManagerException object.</summary>
/// <summary>Creates a new UserAccountManagerException object.</summary>
/// <param name="message">The message for the object.</param>
/// <returns>A new UserManagerException object.</returns>
public UserManagerException(string message)
/// <returns>A new UserAccountManagerException object.</returns>
public UserAccountManagerException(string message)
: base(message)
{
}
/// <summary>Creates a new UserManagerObject with an internal exception.</summary>
/// <summary>Creates a new UserAccountManager Object with an internal exception.</summary>
/// <param name="message">The message for the object.</param>
/// <param name="inner">The inner exception for the object.</param>
/// <returns>A new UserManagerException with a defined inner exception.</returns>
public UserManagerException(string message, Exception inner)
/// <returns>A new UserAccountManagerException with a defined inner exception.</returns>
public UserAccountManagerException(string message, Exception inner)
: base(message, inner)
{
}
}
/// <summary>A UserExistsException object, used when a user already exists when attempting to create a new one.</summary>
/// <summary>A UserExistsException object, used when a user account already exists when attempting to create a new one.</summary>
[Serializable]
public class UserExistsException : UserManagerException
public class UserAccountExistsException : UserAccountManagerException
{
/// <summary>Creates a new UserExistsException object.</summary>
/// <param name="name">The name of the user that already exists.</param>
/// <returns>A UserExistsException object with the user's name passed in the message.</returns>
public UserExistsException(string name)
: base("User '" + name + "' already exists")
/// <summary>Creates a new UserAccountExistsException object.</summary>
/// <param name="name">The name of the user account that already exists.</param>
/// <returns>A UserAccountExistsException object with the user's name passed in the message.</returns>
public UserAccountExistsException(string name)
: base("User account '" + name + "' already exists")
{
}
}
/// <summary>A UserNotExistException, used when a user does not exist and a query failed as a result of it.</summary>
[Serializable]
public class UserNotExistException : UserManagerException
public class UserAccountNotExistException : UserAccountManagerException
{
/// <summary>Creates a new UserNotExistException object, with the user's name in the message.</summary>
/// <param name="name">The user's name to be pasesd in the message.</param>
/// <returns>A new UserNotExistException object with a message containing the user's name that does not exist.</returns>
public UserNotExistException(string name)
: base("User '" + name + "' does not exist")
/// <summary>Creates a new UserAccountNotExistException object, with the user account name in the message.</summary>
/// <param name="name">The user account name to be pasesd in the message.</param>
/// <returns>A new UserAccountNotExistException object with a message containing the user account name that does not exist.</returns>
public UserAccountNotExistException(string name)
: base("User account '" + name + "' does not exist")
{
}
}
/// <summary>A GroupNotExistsException, used when a group does not exist.</summary>
[Serializable]
public class GroupNotExistsException : UserManagerException
public class GroupNotExistsException : UserAccountManagerException
{
/// <summary>Creates a new GroupNotExistsException object with the group's name in the message.</summary>
/// <param name="group">The group name.</param>
@ -643,4 +709,4 @@ namespace TShockAPI.DB
{
}
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -61,7 +61,7 @@ namespace TShockAPI.DB
/// <param name="x">The X position.</param>
/// <param name="y">The Y position.</param>
/// <param name="name">The name.</param>
/// <returns>Whether the opration succeeded.</returns>
/// <returns>Whether the operation succeeded.</returns>
public bool Add(int x, int y, string name)
{
try

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -23,6 +23,9 @@ using System.Diagnostics.CodeAnalysis;
namespace TShockAPI.DB
{
/// <summary>
/// Database extensions
/// </summary>
public static class DbExt
{
/// <summary>
@ -103,7 +106,7 @@ namespace TShockAPI.DB
public static IDbConnection CloneEx(this IDbConnection conn)
{
var clone = (IDbConnection) Activator.CreateInstance(conn.GetType());
var clone = (IDbConnection)Activator.CreateInstance(conn.GetType());
clone.ConnectionString = conn.ConnectionString;
return clone;
}
@ -120,80 +123,84 @@ namespace TShockAPI.DB
private static readonly Dictionary<Type, Func<IDataReader, int, object>> ReadFuncs = new Dictionary
<Type, Func<IDataReader, int, object>>
{
{
typeof (bool),
(s, i) => s.GetBoolean(i)
},
{
typeof (bool?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetBoolean(i)
},
{
typeof (byte),
(s, i) => s.GetByte(i)
},
{
typeof (byte?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetByte(i)
},
{
typeof (Int16),
(s, i) => s.GetInt16(i)
},
{
typeof (Int16?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt16(i)
},
{
typeof (Int32),
(s, i) => s.GetInt32(i)
},
{
typeof (Int32?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt32(i)
},
{
typeof (Int64),
(s, i) => s.GetInt64(i)
},
{
typeof (Int64?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt64(i)
},
{
typeof (string),
(s, i) => s.GetString(i)
},
{
typeof (decimal),
(s, i) => s.GetDecimal(i)
},
{
typeof (decimal?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetDecimal(i)
},
{
typeof (float),
(s, i) => s.GetFloat(i)
},
{
typeof (float?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetFloat(i)
},
{
typeof (double),
(s, i) => s.GetDouble(i)
},
{
typeof (double?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetDouble(i)
},
{
typeof (object),
(s, i) => s.GetValue(i)
},
};
{
{
typeof (bool),
(s, i) => s.GetBoolean(i)
},
{
typeof (bool?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetBoolean(i)
},
{
typeof (byte),
(s, i) => s.GetByte(i)
},
{
typeof (byte?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetByte(i)
},
{
typeof (Int16),
(s, i) => s.GetInt16(i)
},
{
typeof (Int16?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt16(i)
},
{
typeof (Int32),
(s, i) => s.GetInt32(i)
},
{
typeof (Int32?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt32(i)
},
{
typeof (Int64),
(s, i) => s.GetInt64(i)
},
{
typeof (Int64?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetInt64(i)
},
{
typeof (string),
(s, i) => s.GetString(i)
},
{
typeof (decimal),
(s, i) => s.GetDecimal(i)
},
{
typeof (decimal?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetDecimal(i)
},
{
typeof (float),
(s, i) => s.GetFloat(i)
},
{
typeof (float?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetFloat(i)
},
{
typeof (double),
(s, i) => s.GetDouble(i)
},
{
typeof (double?),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetDouble(i)
},
{
typeof (DateTime),
(s, i) => s.IsDBNull(i) ? null : (object)s.GetDateTime(i)
},
{
typeof (object),
(s, i) => s.GetValue(i)
},
};
public static T Get<T>(this IDataReader reader, string column)
{
@ -205,8 +212,8 @@ namespace TShockAPI.DB
if (reader.IsDBNull(column))
return default(T);
if (ReadFuncs.ContainsKey(typeof (T)))
return (T) ReadFuncs[typeof (T)](reader, column);
if (ReadFuncs.ContainsKey(typeof(T)))
return (T)ReadFuncs[typeof(T)](reader, column);
throw new NotImplementedException();
}
@ -272,4 +279,4 @@ namespace TShockAPI.DB
return Reader.Get<T>(Reader.GetOrdinal(column));
}
}
}
}

View file

@ -1,3 +1,21 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace TShockAPI.Extensions
{

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,7 +26,7 @@ namespace TShockAPI
public class FileTools
{
private const string MotdFormat =
"This server is running TShock for Terraria.\n Type /help for a list of commands.\n%255,000,000%Current map: %map%\nCurrent players: %players%";
"This is [c/FF0000:%map%] on [c/00FFFF:TShock for Terraria].\n[c/00FF00:Current players:] [c/FFFF00:%players%]\nType [c/FF0000:/help] for a list of commands.\n";
/// <summary>
/// Path to the file containing the rules.
/// </summary>
@ -154,7 +154,7 @@ namespace TShockAPI
{
if (string.IsNullOrWhiteSpace(line))
continue;
contains = TShock.Utils.GetIPv4Address(line).Equals(ip);
contains = TShock.Utils.GetIPv4AddressFromHostname(line).Equals(ip);
if (contains)
return true;
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,21 +21,21 @@ namespace TShockAPI.Hooks
{
public class AccountDeleteEventArgs
{
public User User { get; private set; }
public UserAccount Account { get; private set; }
public AccountDeleteEventArgs(User user)
public AccountDeleteEventArgs(UserAccount account)
{
this.User = user;
this.Account = account;
}
}
public class AccountCreateEventArgs
{
public User User { get; private set; }
public UserAccount Account { get; private set; }
public AccountCreateEventArgs(User user)
public AccountCreateEventArgs(UserAccount account)
{
this.User = user;
this.Account = account;
}
}
@ -44,7 +44,7 @@ namespace TShockAPI.Hooks
public delegate void AccountCreateD(AccountCreateEventArgs e);
public static event AccountCreateD AccountCreate;
public static void OnAccountCreate(User u)
public static void OnAccountCreate(UserAccount u)
{
if (AccountCreate == null)
return;
@ -55,7 +55,7 @@ namespace TShockAPI.Hooks
public delegate void AccountDeleteD(AccountDeleteEventArgs e);
public static event AccountDeleteD AccountDelete;
public static void OnAccountDelete(User u)
public static void OnAccountDelete(UserAccount u)
{
if (AccountDelete == null)
return;

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Collections.Generic;
using System.ComponentModel;
using TShockAPI.DB;
namespace TShockAPI.Hooks
{
@ -142,7 +143,7 @@ namespace TShockAPI.Hooks
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PlayerPermission"/> event.
/// </summary>
public class PlayerPermissionEventArgs : HandledEventArgs
public class PlayerPermissionEventArgs
{
/// <summary>
/// The player who fired the event.
@ -154,6 +155,11 @@ namespace TShockAPI.Hooks
/// </summary>
public string Permission { get; set; }
/// <summary>
/// <see cref="PermissionHookResult"/> of the hook.
/// </summary>
public PermissionHookResult Result { get; set; }
/// <summary>
/// Initializes a new instance of the PlayerPermissionEventArgs class.
/// </summary>
@ -163,6 +169,106 @@ namespace TShockAPI.Hooks
{
Player = player;
Permission = permission;
Result = PermissionHookResult.Unhandled;
}
}
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PlayerItembanPermission"/> event.
/// </summary>
public class PlayerItembanPermissionEventArgs
{
/// <summary>
/// The player who fired the event.
/// </summary>
public TSPlayer Player { get; set; }
/// <summary>
/// The banned item being checked.
/// </summary>
public ItemBan BannedItem { get; set; }
/// <summary>
/// <see cref="PermissionHookResult"/> of the hook.
/// </summary>
public PermissionHookResult Result { get; set; }
/// <summary>
/// Initializes a new instance of the PlayerItembanPermissionEventArgs class.
/// </summary>
/// <param name="player">The player who fired the event.</param>
/// <param name="bannedItem">The banned item being checked.</param>
public PlayerItembanPermissionEventArgs(TSPlayer player, ItemBan bannedItem)
{
Player = player;
BannedItem = bannedItem;
Result = PermissionHookResult.Unhandled;
}
}
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PlayerProjbanPermission"/> event.
/// </summary>
public class PlayerProjbanPermissionEventArgs
{
/// <summary>
/// The player who fired the event.
/// </summary>
public TSPlayer Player { get; set; }
/// <summary>
/// The banned projectile being checked.
/// </summary>
public ProjectileBan BannedProjectile { get; set; }
/// <summary>
/// <see cref="PermissionHookResult"/> of the hook.
/// </summary>
public PermissionHookResult Result { get; set; }
/// <summary>
/// Initializes a new instance of the PlayerProjbanPermissionEventArgs class.
/// </summary>
/// <param name="player">The player who fired the event.</param>
/// <param name="checkedProjectile">The banned projectile being checked.</param>
public PlayerProjbanPermissionEventArgs(TSPlayer player, ProjectileBan checkedProjectile)
{
Player = player;
BannedProjectile = checkedProjectile;
Result = PermissionHookResult.Unhandled;
}
}
/// <summary>
/// EventArgs used for the <see cref="PlayerHooks.PlayerTilebanPermission"/> event.
/// </summary>
public class PlayerTilebanPermissionEventArgs
{
/// <summary>
/// The player who fired the event.
/// </summary>
public TSPlayer Player { get; set; }
/// <summary>
/// The banned tile being checked.
/// </summary>
public TileBan BannedTile { get; set; }
/// <summary>
/// <see cref="PermissionHookResult"/> of the hook.
/// </summary>
public PermissionHookResult Result { get; set; }
/// <summary>
/// Initializes a new instance of the PlayerTilebanPermissionEventArgs class.
/// </summary>
/// <param name="player">The player who fired the event.</param>
/// <param name="checkedTile">The banned tile being checked.</param>
public PlayerTilebanPermissionEventArgs(TSPlayer player, TileBan checkedTile)
{
Player = player;
BannedTile = checkedTile;
Result = PermissionHookResult.Unhandled;
}
}
@ -232,6 +338,37 @@ namespace TShockAPI.Hooks
/// </summary>
public static event PlayerPermissionD PlayerPermission;
/// <summary>
/// The delegate of the <see cref="PlayerItembanPermission"/> event.
/// </summary>
/// <param name="e">The EventArgs for this event.</param>
public delegate void PlayerItembanPermissionD(PlayerItembanPermissionEventArgs e);
/// <summary>
/// Fired by players every time a permission check on banned items involving them occurs.
/// </summary>
public static event PlayerItembanPermissionD PlayerItembanPermission;
/// <summary>
/// The delegate of the <see cref="PlayerProjbanPermission"/> event.
/// </summary>
/// <param name="e">The EventArgs for this event.</param>
public delegate void PlayerProjbanPermissionD(PlayerProjbanPermissionEventArgs e);
/// <summary>
/// Fired by players every time a permission check on banned projectiles involving them occurs.
/// </summary>
public static event PlayerProjbanPermissionD PlayerProjbanPermission;
/// <summary>
/// The delegate of the <see cref="PlayerTilebanPermission"/> event.
/// </summary>
/// <param name="e">The EventArgs for this event.</param>
public delegate void PlayerTilebanPermissionD(PlayerTilebanPermissionEventArgs e);
/// <summary>
/// Fired by players every time a permission check on banned tiles involving them occurs.
/// </summary>
public static event PlayerTilebanPermissionD PlayerTilebanPermission;
/// <summary>
/// Fires the <see cref="PlayerPostLogin"/> event.
/// </summary>
@ -326,15 +463,79 @@ namespace TShockAPI.Hooks
/// Fires the <see cref="PlayerPermission"/> event.
/// </summary>
/// <param name="player">The player firing the event.</param>
/// <returns>True if the event has been handled.</returns>
public static bool OnPlayerPermission(TSPlayer player, string permission)
/// <returns>Event result if the event has been handled, otherwise <see cref="PermissionHookResult.Unhandled"/>.</returns>
public static PermissionHookResult OnPlayerPermission(TSPlayer player, string permission)
{
if (PlayerPermission == null)
return false;
return PermissionHookResult.Unhandled;
var args = new PlayerPermissionEventArgs(player, permission);
PlayerPermission(args);
return args.Handled;
return args.Result;
}
/// <summary>
/// Fires the <see cref="PlayerItembanPermission"/> event.
/// </summary>
/// <param name="player">The player firing the event.</param>
/// <returns>Event result if the event has been handled, otherwise <see cref="PermissionHookResult.Unhandled"/>.</returns>
public static PermissionHookResult OnPlayerItembanPermission(TSPlayer player, ItemBan bannedItem)
{
if (PlayerItembanPermission == null)
return PermissionHookResult.Unhandled;
var args = new PlayerItembanPermissionEventArgs(player, bannedItem);
PlayerItembanPermission(args);
return args.Result;
}
/// <summary>
/// Fires the <see cref="PlayerProjbanPermission"/> event.
/// </summary>
/// <param name="player">The player firing the event.</param>
/// <returns>Event result if the event has been handled, otherwise <see cref="PermissionHookResult.Unhandled"/>.</returns>
public static PermissionHookResult OnPlayerProjbanPermission(TSPlayer player, ProjectileBan bannedProj)
{
if (PlayerProjbanPermission == null)
return PermissionHookResult.Unhandled;
var args = new PlayerProjbanPermissionEventArgs(player, bannedProj);
PlayerProjbanPermission(args);
return args.Result;
}
/// <summary>
/// Fires the <see cref="PlayerTilebanPermission"/> event.
/// </summary>
/// <param name="player">The player firing the event.</param>
/// <returns>Event result if the event has been handled, otherwise <see cref="PermissionHookResult.Unhandled"/>.</returns>
public static PermissionHookResult OnPlayerTilebanPermission(TSPlayer player, TileBan bannedTile)
{
if (PlayerTilebanPermission == null)
return PermissionHookResult.Unhandled;
var args = new PlayerTilebanPermissionEventArgs(player, bannedTile);
PlayerTilebanPermission(args);
return args.Result;
}
}
/// <summary>
/// Defines the possible outcomes of <see cref="PlayerHooks.PlayerPermission"/> handlers.
/// </summary>
public enum PermissionHookResult
{
/// <summary>Hook doesn't return a result on the permission check.</summary>
Unhandled,
/// <summary>Permission is explicitly denied by a hook.</summary>
Denied,
/// <summary>Permission is explicitly granted by a hook.</summary>
Granted
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -110,5 +110,29 @@ namespace TShockAPI.Hooks
RegionDeleted(new RegionDeletedEventArgs(region));
}
public class RegionRenamedEventArgs
{
public Region Region { get; private set; }
public string OldName { get; private set; }
public string NewName { get; private set; }
public RegionRenamedEventArgs(Region region, string oldName, string newName)
{
Region = region;
OldName = oldName;
NewName = newName;
}
}
public delegate void RegionRenamedD(RegionRenamedEventArgs args);
public static event RegionRenamedD RegionRenamed;
public static void OnRegionRenamed(Region region, string oldName, string newName)
{
if (RegionRenamed == null)
return;
RegionRenamed(new RegionRenamedEventArgs(region, oldName, newName));
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

216
TShockAPI/ItemBans.cs Normal file
View file

@ -0,0 +1,216 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2018 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Terraria.ID;
using TShockAPI.DB;
using TShockAPI.Net;
using Terraria;
using Microsoft.Xna.Framework;
using OTAPI.Tile;
using TShockAPI.Localization;
using static TShockAPI.GetDataHandlers;
using TerrariaApi.Server;
using Terraria.ObjectData;
using Terraria.DataStructures;
using Terraria.Localization;
using System.Data;
namespace TShockAPI
{
/// <summary>The TShock item ban subsystem. It handles keeping things out of people's inventories.</summary>
internal sealed class ItemBans
{
/// <summary>The database connection layer to for the item ban subsystem.</summary>
private ItemManager DataModel;
/// <summary>The last time the second update process was run. Used to throttle task execution.</summary>
private DateTime LastTimelyRun = DateTime.UtcNow;
/// <summary>A reference to the TShock plugin so we can register events.</summary>
private TShock Plugin;
/// <summary>Creates an ItemBan system given a plugin to register events to and a database.</summary>
/// <param name="plugin">The executing plugin.</param>
/// <param name="database">The database the item ban information is stored in.</param>
/// <returns>A new item ban system.</returns>
internal ItemBans(TShock plugin, IDbConnection database)
{
DataModel = new ItemManager(database);
Plugin = plugin;
ServerApi.Hooks.GameUpdate.Register(plugin, OnGameUpdate);
GetDataHandlers.PlayerUpdate += OnPlayerUpdate;
GetDataHandlers.ChestItemChange += OnChestItemChange;
}
/// <summary>Called on the game update loop (the XNA tickrate).</summary>
/// <param name="args">The standard event arguments.</param>
internal void OnGameUpdate(EventArgs args)
{
if ((DateTime.UtcNow - LastTimelyRun).TotalSeconds >= 1)
{
OnSecondlyUpdate(args);
}
}
/// <summary>Called by OnGameUpdate once per second to execute tasks regularly but not too often.</summary>
/// <param name="args">The standard event arguments.</param>
internal void OnSecondlyUpdate(EventArgs args)
{
DisableFlags disableFlags = TShock.Config.DisableSecondUpdateLogs ? DisableFlags.WriteToConsole : DisableFlags.WriteToLogAndConsole;
foreach (TSPlayer player in TShock.Players)
{
if (player == null || !player.Active)
{
continue;
}
// Untaint now, re-taint if they fail the check.
UnTaint(player);
// No matter the player type, we do a check when a player is holding an item that's banned.
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(player.TPlayer.inventory[player.TPlayer.selectedItem].netID), player))
{
string itemName = player.TPlayer.inventory[player.TPlayer.selectedItem].Name;
player.Disable($"holding banned item: {itemName}", disableFlags);
SendCorrectiveMessage(player, itemName);
}
// If SSC isn't enabled OR if SSC is enabled and the player is logged in
// In a case like this, we do the full check too.
if (!Main.ServerSideCharacter || (Main.ServerSideCharacter && player.IsLoggedIn))
{
// The Terraria inventory is composed of a multicultural set of arrays
// with various different contents and beliefs
// Armor ban checks
foreach (Item item in player.TPlayer.armor)
{
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), player))
{
Taint(player);
SendCorrectiveMessage(player, item.Name);
}
}
// Dye ban checks
foreach (Item item in player.TPlayer.dye)
{
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), player))
{
Taint(player);
SendCorrectiveMessage(player, item.Name);
}
}
// Misc equip ban checks
foreach (Item item in player.TPlayer.miscEquips)
{
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), player))
{
Taint(player);
SendCorrectiveMessage(player, item.Name);
}
}
// Misc dye ban checks
foreach (Item item in player.TPlayer.miscDyes)
{
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), player))
{
Taint(player);
SendCorrectiveMessage(player, item.Name);
}
}
}
}
// Set the update time to now, so that we know when to execute next.
// We do this at the end so that the task can't re-execute faster than we expected.
// (If we did this at the start of the method, the method execution would count towards the timer.)
LastTimelyRun = DateTime.UtcNow;
}
internal void OnPlayerUpdate(object sender, PlayerUpdateEventArgs args)
{
DisableFlags disableFlags = TShock.Config.DisableSecondUpdateLogs ? DisableFlags.WriteToConsole : DisableFlags.WriteToLogAndConsole;
bool useItem = ((BitsByte) args.Control)[5];
TSPlayer player = args.Player;
string itemName = player.TPlayer.inventory[args.Item].Name;
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(player.TPlayer.inventory[args.Item].netID), args.Player))
{
player.TPlayer.controlUseItem = false;
player.Disable($"holding banned item: {itemName}", disableFlags);
SendCorrectiveMessage(player, itemName);
player.TPlayer.Update(player.TPlayer.whoAmI);
NetMessage.SendData((int)PacketTypes.PlayerUpdate, -1, player.Index, NetworkText.Empty, player.Index);
args.Handled = true;
return;
}
args.Handled = false;
return;
}
internal void OnChestItemChange(object sender, ChestItemEventArgs args)
{
Item item = new Item();
item.netDefaults(args.Type);
if (DataModel.ItemIsBanned(EnglishLanguage.GetItemNameById(item.type), args.Player))
{
SendCorrectiveMessage(args.Player, item.Name);
args.Handled = true;
return;
}
args.Handled = false;
return;
}
private void UnTaint(TSPlayer player)
{
player.IsDisabledForBannedWearable = false;
}
private void Taint(TSPlayer player)
{
// Arbitrarily does things to the player
player.SetBuff(BuffID.Frozen, 330, true);
player.SetBuff(BuffID.Stoned, 330, true);
player.SetBuff(BuffID.Webbed, 330, true);
// Marks them as a target for future disables
player.IsDisabledForBannedWearable = true;
}
private void SendCorrectiveMessage(TSPlayer player, string itemName)
{
player.SendErrorMessage("{0} is banned! Remove it!", itemName);
}
}
}

View file

@ -1,3 +1,21 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -23,8 +23,12 @@ using System.IO;
using System.Linq;
using System.Text;
// Since the permission nodes have annotations that say what they are, we don't need XML comments.
#pragma warning disable 1591
namespace TShockAPI
{
/// <summary>Contains the permission nodes used in TShock.</summary>
public static class Permissions
{
// tshock.account nodes
@ -125,8 +129,8 @@ namespace TShockAPI
[Description("User can reload the configurations file.")]
public static readonly string cfgreload = "tshock.cfg.reload";
[Description("User can download updates to plugins that are currently running.")]
public static readonly string updateplugins = "tshock.cfg.updateplugins";
[Description("User can create reference files of Terraria IDs and the permission matrix in the server folder.")]
public static readonly string createdumps = "tshock.cfg.createdumps";
// tshock.ignore nodes
@ -145,9 +149,6 @@ namespace TShockAPI
[Description("Prevents you from being disabled by paint abuse detection.")]
public static readonly string ignorepaintdetection = "tshock.ignore.paint";
[Description("Prevents you from being reverted by no clip detection.")]
public static readonly string ignorenoclipdetection = "tshock.ignore.noclip";
[Description("Prevents you from being disabled by stack hack detection.")]
public static readonly string ignorestackhackdetection = "tshock.ignore.itemstack";
@ -221,6 +222,9 @@ namespace TShockAPI
[Description("Meant for super admins only.")]
public static readonly string user = "tshock.superadmin.user";
[Description("Allows a user to elevate to superadmin for 10 minutes.")]
public static readonly string su = "tshock.su";
// tshock.tp nodes
[Description("User can teleport *everyone* to them.")]
@ -397,6 +401,7 @@ namespace TShockAPI
[Description("Player can see advanced information about any user account.")]
public static readonly string advaccountinfo = "tshock.accountinfo.details";
/// <summary>
/// Lists all commands associated with a given permission
/// </summary>
@ -445,19 +450,4 @@ namespace TShockAPI
File.WriteAllText("PermissionsDescriptions.txt", sb.ToString());
}
}
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class TodoAttribute : Attribute
{
public string Info { get; private set; }
public TodoAttribute(string info)
{
Info = info;
}
public TodoAttribute()
{
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -26,9 +26,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("TShock for Terraria")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Nyx Studios & TShock Contributors")]
[assembly: AssemblyCompany("Pryaxis & TShock Contributors")]
[assembly: AssemblyProduct("TShockAPI")]
[assembly: AssemblyCopyright("Copyright © Nyx Studios 2011-2017")]
[assembly: AssemblyCopyright("Copyright © Pryaxis & TShock Contributors 2011-2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

197
TShockAPI/RegionHandler.cs Normal file
View file

@ -0,0 +1,197 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using TShockAPI.DB;
using TShockAPI.Hooks;
namespace TShockAPI
{
/// <summary>
/// Represents TShock's Region subsystem. This subsystem is in charge of executing region related logic, such as
/// setting temp points or invoking region events.
/// </summary>
internal sealed class RegionHandler : IDisposable
{
private readonly RegionManager _regionManager;
/// <summary>
/// Initializes a new instance of the <see cref="RegionHandler"/> class with the specified <see cref="RegionManager"/> instance.
/// </summary>
/// <param name="regionManager">The <see cref="RegionManager"/> instance.</param>
public RegionHandler(RegionManager regionManager)
{
_regionManager = regionManager;
GetDataHandlers.GemLockToggle += OnGemLockToggle;
GetDataHandlers.PlayerUpdate += OnPlayerUpdate;
GetDataHandlers.TileEdit += OnTileEdit;
}
/// <summary>
/// Disposes the region handler.
/// </summary>
public void Dispose()
{
GetDataHandlers.GemLockToggle -= OnGemLockToggle;
GetDataHandlers.PlayerUpdate -= OnPlayerUpdate;
GetDataHandlers.TileEdit -= OnTileEdit;
}
private void OnGemLockToggle(object sender, GetDataHandlers.GemLockToggleEventArgs e)
{
if (TShock.Config.RegionProtectGemLocks)
{
e.Handled = true;
}
}
private void OnPlayerUpdate(object sender, GetDataHandlers.PlayerUpdateEventArgs e)
{
var player = e.Player;
// Store the player's last known region and update the current based on known regions at their coordinates.
var oldRegion = player.CurrentRegion;
player.CurrentRegion = _regionManager.GetTopRegion(_regionManager.InAreaRegion(player.TileX, player.TileY));
// Do not fire any hooks if the player has not left and/or entered a region.
if (player.CurrentRegion == oldRegion)
{
return;
}
// Ensure that the player has left a region before invoking the RegionLeft event
if (oldRegion != null)
{
RegionHooks.OnRegionLeft(player, oldRegion);
}
// Ensure that the player has entered a valid region before invoking the RegionEntered event
if (player.CurrentRegion != null)
{
RegionHooks.OnRegionEntered(player, player.CurrentRegion);
}
}
private void OnTileEdit(object sender, GetDataHandlers.TileEditEventArgs e)
{
var player = e.Player;
#region Region Information Display
if (player.AwaitingName)
{
bool includeUnprotected = false;
bool includeZIndexes = false;
bool persistentMode = false;
foreach (string nameParameter in player.AwaitingNameParameters)
{
// If this flag is passed the final output will include unprotected regions, i.e regions
// that have the DisableBuild flag set to false
if (nameParameter.Equals("-u", StringComparison.InvariantCultureIgnoreCase))
{
includeUnprotected = true;
}
// If this flag is passed the final output will include a region's Z index
if (nameParameter.Equals("-z", StringComparison.InvariantCultureIgnoreCase))
{
includeZIndexes = true;
}
// If this flag is passed the player will continue to receive region information upon editing tiles
if (nameParameter.Equals("-p", StringComparison.InvariantCultureIgnoreCase))
{
persistentMode = true;
}
}
var output = new List<string>();
foreach (Region region in _regionManager.Regions.OrderBy(r => r.Z).Reverse())
{
// Ensure that the specified tile is region protected
if (e.X < region.Area.Left || e.X > region.Area.Right)
{
continue;
}
if (e.Y < region.Area.Top || e.Y > region.Area.Bottom)
{
continue;
}
// Do not include the current region if it has not been protected and the includeUnprotected flag has not been set
if (!region.DisableBuild && !includeUnprotected)
{
continue;
}
output.Add($"{region.Name} {(includeZIndexes ? $"(Z:{region.Z}" : string.Empty)}");
}
if (output.Count == 0)
{
player.SendInfoMessage(includeUnprotected
? "There are no regions at this point."
: "There are no regions at this point, or they are not protected.");
}
else
{
player.SendInfoMessage(includeUnprotected ? "Regions at this point: " : "Protected regions at this point: ");
foreach (string line in PaginationTools.BuildLinesFromTerms(output))
{
player.SendMessage(line, Color.White);
}
}
if (!persistentMode)
{
player.AwaitingName = false;
player.AwaitingNameParameters = null;
}
// Revert all tile changes and handle the event
player.SendTileSquare(e.X, e.Y, 4);
e.Handled = true;
}
#endregion
#region TempPoints Setup
if (player.AwaitingTempPoint != 0)
{
// Set temp point coordinates to current tile coordinates
player.TempPoints[player.AwaitingTempPoint - 1].X = e.X;
player.TempPoints[player.AwaitingTempPoint - 1].Y = e.Y;
player.SendInfoMessage($"Set temp point {player.AwaitingTempPoint}.");
// Reset the awaiting temp point
player.AwaitingTempPoint = 0;
// Revert all tile changes and handle the event
player.SendTileSquare(e.X, e.Y, 4);
e.Handled = true;
}
#endregion
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -250,30 +250,6 @@ namespace Rests
}
}
#region Event
[Obsolete("This class will be removed in the next release")]
public class RestRequestEventArgs : HandledEventArgs
{
public RequestEventArgs Request { get; set; }
}
[Obsolete("This method will be removed in the next release")]
public static HandlerList<RestRequestEventArgs> RestRequestEvent;
private static bool OnRestRequestCall(RequestEventArgs request)
{
if (RestRequestEvent == null)
return false;
var args = new RestRequestEventArgs
{
Request = request,
};
RestRequestEvent.Invoke(null, args);
return args.Handled;
}
#endregion
/// <summary>
/// Called when the <see cref="HttpListener"/> receives a request
/// </summary>
@ -285,9 +261,6 @@ namespace Rests
if (obj == null)
throw new NullReferenceException("obj");
if (OnRestRequestCall(e))
return;
var str = JsonConvert.SerializeObject(obj, Formatting.Indented);
var jsonp = e.Request.Parameters["jsonp"];
if (!string.IsNullOrWhiteSpace(jsonp))

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -205,7 +205,6 @@ namespace TShockAPI
Rest.RegisterRedirect("/server/broadcast", "/v2/server/broadcast");
Rest.RegisterRedirect("/server/reload", "/v2/server/reload");
Rest.RegisterRedirect("/server/off", "/v2/server/off");
Rest.RegisterRedirect("/server/restart", "/v3/server/restart");
Rest.RegisterRedirect("/server/rawcmd", "/v3/server/rawcmd");
//user commands
@ -247,7 +246,6 @@ namespace TShockAPI
Rest.Register(new SecureRestCommand("/v2/server/broadcast", ServerBroadcast));
Rest.Register(new SecureRestCommand("/v3/server/reload", ServerReload, RestPermissions.restcfg));
Rest.Register(new SecureRestCommand("/v2/server/off", ServerOff, RestPermissions.restmaintenance));
Rest.Register(new SecureRestCommand("/v3/server/restart", ServerRestart, RestPermissions.restmaintenance));
Rest.Register(new SecureRestCommand("/v3/server/rawcmd", ServerCommandV3, RestPermissions.restrawcommand));
Rest.Register(new SecureRestCommand("/tokentest", ServerTokenTest));
@ -335,32 +333,14 @@ namespace TShockAPI
return RestResponse("The server is shutting down");
}
[Description("Attempt to restart the server.")]
[Route("/v3/server/restart")]
[Permission(RestPermissions.restmaintenance)]
[Noun("confirm", true, "Confirm that you actually want to restart the server", typeof(bool))]
[Noun("message", false, "The shutdown message.", typeof(String))]
[Noun("nosave", false, "Shutdown without saving.", typeof(bool))]
[Token]
private object ServerRestart(RestRequestArgs args)
{
if (!GetBool(args.Parameters["confirm"], false))
return RestInvalidParam("confirm");
// Inform players the server is shutting down
var reason = string.IsNullOrWhiteSpace(args.Parameters["message"]) ? "Server is restarting" : args.Parameters["message"];
TShock.Utils.RestartServer(!GetBool(args.Parameters["nosave"], false), reason);
return RestResponse("The server is shutting down and will attempt to restart");
}
[Description("Reload config files for the server.")]
[Route("/v3/server/reload")]
[Permission(RestPermissions.restcfg)]
[Token]
private object ServerReload(RestRequestArgs args)
{
TShock.Utils.Reload(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName)));
TShock.Utils.Reload();
Hooks.GeneralHooks.OnReloadEvent(new TSRestPlayer(args.TokenData.Username, TShock.Groups.GetGroupByName(args.TokenData.UserGroupName)));
return RestResponse("Configuration, permissions, and regions reload complete. Some changes may require a server restart.");
}
@ -431,7 +411,7 @@ namespace TShockAPI
var players = new ArrayList();
foreach (TSPlayer tsPlayer in TShock.Players.Where(p => null != p))
{
var p = PlayerFilter(tsPlayer, args.Parameters, ((args.TokenData.UserGroupName) != "" && TShock.Utils.GetGroup(args.TokenData.UserGroupName).HasPermission(RestPermissions.viewips)));
var p = PlayerFilter(tsPlayer, args.Parameters, ((args.TokenData.UserGroupName) != "" && TShock.Groups.GetGroupByName(args.TokenData.UserGroupName).HasPermission(RestPermissions.viewips)));
if (null != p)
players.Add(p);
}
@ -482,7 +462,7 @@ namespace TShockAPI
[Token]
private object UserActiveListV2(RestRequestArgs args)
{
return new RestObject() { { "activeusers", string.Join("\t", TShock.Players.Where(p => null != p && null != p.User && p.Active).Select(p => p.User.Name)) } };
return new RestObject() { { "activeusers", string.Join("\t", TShock.Players.Where(p => null != p && null != p.Account && p.Active).Select(p => p.Account.Name)) } };
}
[Description("Lists all user accounts in the TShock database.")]
@ -491,7 +471,7 @@ namespace TShockAPI
[Token]
private object UserListV2(RestRequestArgs args)
{
return new RestObject() { { "users", TShock.Users.GetUsers().Select(p => new Dictionary<string,object>(){
return new RestObject() { { "users", TShock.UserAccounts.GetUserAccounts().Select(p => new Dictionary<string,object>(){
{"name", p.Name},
{"id", p.ID},
{"group", p.Group},
@ -520,10 +500,11 @@ namespace TShockAPI
return RestMissingParam("password");
// NOTE: ip can be blank
User user = new User(username, password, "", group, "", "", "");
UserAccount account = new UserAccount(username, "", "", group, "", "", "");
try
{
TShock.Users.AddUser(user);
account.CreateBCryptHash(password);
TShock.UserAccounts.AddUserAccount(account);
}
catch (Exception e)
{
@ -552,13 +533,13 @@ namespace TShockAPI
if (string.IsNullOrWhiteSpace(group) && string.IsNullOrWhiteSpace(password))
return RestMissingParam("group", "password");
User user = (User)ret;
UserAccount account = (UserAccount)ret;
var response = new RestObject();
if (!string.IsNullOrWhiteSpace(password))
{
try
{
TShock.Users.SetUserPassword(user, password);
TShock.UserAccounts.SetUserAccountPassword(account, password);
response.Add("password-response", "Password updated successfully");
}
catch (Exception e)
@ -571,7 +552,7 @@ namespace TShockAPI
{
try
{
TShock.Users.SetUserGroup(user, group);
TShock.UserAccounts.SetUserGroup(account, group);
response.Add("group-response", "Group updated successfully");
}
catch (Exception e)
@ -597,7 +578,7 @@ namespace TShockAPI
try
{
TShock.Users.RemoveUser((User)ret);
TShock.UserAccounts.RemoveUserAccount((UserAccount)ret);
}
catch (Exception e)
{
@ -619,8 +600,8 @@ namespace TShockAPI
if (ret is RestObject)
return ret;
User user = (User)ret;
return new RestObject() { { "group", user.Group }, { "id", user.ID.ToString() }, { "name", user.Name } };
UserAccount account = (UserAccount)ret;
return new RestObject() { { "group", account.Group }, { "id", account.ID.ToString() }, { "name", account.Name } };
}
#endregion
@ -644,7 +625,7 @@ namespace TShockAPI
try
{
TShock.Bans.AddBan(ip, name, "", args.Parameters["reason"], true, args.TokenData.Username);
TShock.Bans.AddBan(ip, name, "", "", args.Parameters["reason"], true, args.TokenData.Username);
}
catch (Exception e)
{
@ -937,10 +918,10 @@ namespace TShockAPI
return new RestObject()
{
{"nickname", player.Name},
{"username", player.User?.Name},
{"username", player.Account?.Name},
{"ip", player.IP},
{"group", player.Group.Name},
{"registered", player.User?.Registered},
{"registered", player.Account?.Registered},
{"muted", player.mute },
{"position", player.TileX + "," + player.TileY},
{"inventory", string.Join(", ", inventory.Select(p => (p.Name + ":" + p.stack)))},
@ -978,10 +959,10 @@ namespace TShockAPI
return new RestObject
{
{"nickname", player.Name},
{"username", player.User?.Name},
{"username", player.Account?.Name},
{"ip", player.IP},
{"group", player.Group.Name},
{"registered", player.User?.Registered},
{"registered", player.Account?.Registered},
{"muted", player.mute },
{"position", player.TileX + "," + player.TileY},
{"items", items},
@ -1002,7 +983,7 @@ namespace TShockAPI
return ret;
TSPlayer player = (TSPlayer)ret;
TShock.Utils.ForceKick(player, null == args.Parameters["reason"] ? "Kicked via web" : args.Parameters["reason"], false, true);
player.Kick(null == args.Parameters["reason"] ? "Kicked via web" : args.Parameters["reason"], false, true, null, true);
return RestResponse("Player " + player.Name + " was kicked");
}
@ -1021,8 +1002,8 @@ namespace TShockAPI
TSPlayer player = (TSPlayer)ret;
var reason = null == args.Parameters["reason"] ? "Banned via web" : args.Parameters["reason"];
TShock.Bans.AddBan(player.IP, player.Name, "", reason);
TShock.Utils.ForceKick(player, reason, false, true);
TShock.Bans.AddBan(player.IP, player.Name, "", "", reason);
player.Kick(reason, true, false, null, true);
return RestResponse("Player " + player.Name + " was banned");
}
@ -1264,7 +1245,7 @@ namespace TShockAPI
if (string.IsNullOrWhiteSpace(name))
return RestMissingParam("player");
var found = TShock.Utils.FindPlayer(name);
var found = TSPlayer.FindByNameOrID(name);
switch(found.Count)
{
case 1:
@ -1282,7 +1263,7 @@ namespace TShockAPI
if (string.IsNullOrWhiteSpace(name))
return RestMissingParam("user");
User user;
UserAccount account;
string type = parameters["type"];
try
{
@ -1291,10 +1272,10 @@ namespace TShockAPI
case null:
case "name":
type = "name";
user = TShock.Users.GetUserByName(name);
account = TShock.UserAccounts.GetUserAccountByName(name);
break;
case "id":
user = TShock.Users.GetUserByID(Convert.ToInt32(name));
account = TShock.UserAccounts.GetUserAccountByID(Convert.ToInt32(name));
break;
default:
return RestError("Invalid Type: '" + type + "'");
@ -1305,10 +1286,10 @@ namespace TShockAPI
return RestError(e.Message);
}
if (null == user)
if (null == account)
return RestError(String.Format("User {0} '{1}' doesn't exist", type, name));
return user;
return account;
}
private object BanFind(IParameterCollection parameters)
@ -1358,7 +1339,7 @@ namespace TShockAPI
var player = new Dictionary<string, object>
{
{"nickname", tsPlayer.Name},
{"username", tsPlayer.User == null ? "" : tsPlayer.User.Name},
{"username", tsPlayer.Account == null ? "" : tsPlayer.Account.Name},
{"group", tsPlayer.Group.Name},
{"active", tsPlayer.Active},
{"state", tsPlayer.State},

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -18,8 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.ComponentModel;
// Since the permission nodes have annotations that say what they are, we don't need XML comments.
#pragma warning disable 1591
namespace Rests
{
/// <summary>Contains the REST permission nodes used in TShock.</summary>
public static class RestPermissions
{
// tshock.rest.bans nodes

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -131,7 +131,7 @@ namespace Rests
tokenBucket.Add(context.RemoteEndPoint.Address.ToString(), 1); // First time request, set to one and process request
}
User userAccount = TShock.Users.GetUserByName(username);
UserAccount userAccount = TShock.UserAccounts.GetUserAccountByName(username);
if (userAccount == null)
{
AddTokenToBucket(context.RemoteEndPoint.Address.ToString());
@ -144,7 +144,7 @@ namespace Rests
return new RestObject("403") { Error = "Username or password may be incorrect or this account may not have sufficient privileges." };
}
Group userGroup = TShock.Utils.GetGroup(userAccount.Group);
Group userGroup = TShock.Groups.GetGroupByName(userAccount.Group);
if (!userGroup.HasPermission(RestPermissions.restapi) && userAccount.Group != "superadmin")
{
AddTokenToBucket(context.RemoteEndPoint.Address.ToString());
@ -216,4 +216,4 @@ namespace Rests
return result;
}
}
}
}

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,24 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.ComponentModel;
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System.IO;
using System.Linq;
using System.Text;

View file

@ -1,3 +1,21 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Net;
@ -12,23 +30,23 @@ namespace TShockAPI.Sockets
{
public class LinuxTcpSocket : ISocket
{
private byte[] _packetBuffer = new byte[1024];
public byte[] _packetBuffer = new byte[1024];
private int _packetBufferLength;
public int _packetBufferLength;
private List<object> _callbackBuffer = new List<object>();
public List<object> _callbackBuffer = new List<object>();
private int _messagesInQueue;
public int _messagesInQueue;
private TcpClient _connection;
public TcpClient _connection;
private TcpListener _listener;
public TcpListener _listener;
private SocketConnectionAccepted _listenerCallback;
public SocketConnectionAccepted _listenerCallback;
private RemoteAddress _remoteAddress;
public RemoteAddress _remoteAddress;
private bool _isListening;
public bool _isListening;
public int MessagesInQueue
{

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,6 +34,7 @@ using TShockAPI.DB;
using TShockAPI.Hooks;
using TShockAPI.Net;
using Timer = System.Timers.Timer;
using System.Linq;
namespace TShockAPI
{
@ -73,6 +74,43 @@ namespace TShockAPI
/// </summary>
public static readonly TSPlayer All = new TSPlayer("All");
/// <summary>
/// Finds a TSPlayer based on name or ID
/// </summary>
/// <param name="plr">Player name or ID</param>
/// <returns>A list of matching players</returns>
public static List<TSPlayer> FindByNameOrID(string plr)
{
var found = new List<TSPlayer>();
// Avoid errors caused by null search
if (plr == null)
return found;
byte plrID;
if (byte.TryParse(plr, out plrID) && plrID < Main.maxPlayers)
{
TSPlayer player = TShock.Players[plrID];
if (player != null && player.Active)
{
return new List<TSPlayer> { player };
}
}
string plrLower = plr.ToLower();
foreach (TSPlayer player in TShock.Players)
{
if (player != null)
{
// Must be an EXACT match
if (player.Name == plr)
return new List<TSPlayer> { player };
if (player.Name.ToLower().StartsWith(plrLower))
found.Add(player);
}
}
return found;
}
/// <summary>
/// The amount of tiles that the player has killed in the last second.
/// </summary>
@ -190,8 +228,6 @@ namespace TShockAPI
/// </summary>
public DateTime LastThreat { get; set; }
public bool InitSpawn;
/// <summary>
/// Whether the player should see logs.
/// </summary>
@ -218,10 +254,10 @@ namespace TShockAPI
public Vector2 LastNetPosition = Vector2.Zero;
/// <summary>
/// User object associated with the player.
/// UserAccount object associated with the player.
/// Set when the player logs in.
/// </summary>
public User User { get; set; }
public UserAccount Account { get; set; }
/// <summary>
/// Whether the player performed a valid login attempt (i.e. entered valid user name and password) but is still blocked
@ -277,13 +313,244 @@ namespace TShockAPI
private string CacheIP;
public string IgnoreActionsForInventory = "none";
/// <summary>Determines if the player is disabled by the SSC subsystem for not being logged in.</summary>
public bool IsDisabledForSSC = false;
public string IgnoreActionsForCheating = "none";
/// <summary>Determines if the player is disabled by Bouncer for having hacked item stacks.</summary>
public bool IsDisabledForStackDetection = false;
public string IgnoreActionsForDisabledArmor = "none";
/// <summary>Determines if the player is disabled by the item bans system for having banned wearables on the server.</summary>
public bool IsDisabledForBannedWearable = false;
public bool IgnoreActionsForClearingTrashCan;
/// <summary>Determines if the player is disabled for not clearing their trash. A re-login is the only way to reset this.</summary>
public bool IsDisabledPendingTrashRemoval;
/// <summary>Checks to see if active throttling is happening on events by Bouncer. Rejects repeated events by malicious clients in a short window.</summary>
/// <returns>If the player is currently being throttled by Bouncer, or not.</returns>
public bool IsBouncerThrottled()
{
return (DateTime.UtcNow - LastThreat).TotalMilliseconds < 5000;
}
/// <summary>Easy check if a player has any of IsDisabledForSSC, IsDisabledForStackDetection, IsDisabledForBannedWearable, or IsDisabledPendingTrashRemoval set. Or if they're not logged in and a login is required.</summary>
/// <returns>If any of the checks that warrant disabling are set on this player. If true, Disable() is repeatedly called on them.</returns>
public bool IsBeingDisabled()
{
return IsDisabledForSSC
|| IsDisabledForStackDetection
|| IsDisabledForBannedWearable
|| IsDisabledPendingTrashRemoval
|| !IsLoggedIn && TShock.Config.RequireLogin;
}
/// <summary>Checks to see if a player has hacked item stacks in their inventory, and messages them as it checks.</summary>
/// <param name="shouldWarnPlayer">If the check should send a message to the player with the results of the check.</param>
/// <returns>True if any stacks don't conform.</returns>
public bool HasHackedItemStacks(bool shouldWarnPlayer = false)
{
// Iterates through each inventory location a player has.
// This section is sub divided into number ranges for what each range of slots corresponds to.
bool check = false;
Item[] inventory = TPlayer.inventory;
Item[] armor = TPlayer.armor;
Item[] dye = TPlayer.dye;
Item[] miscEquips = TPlayer.miscEquips;
Item[] miscDyes = TPlayer.miscDyes;
Item[] piggy = TPlayer.bank.item;
Item[] safe = TPlayer.bank2.item;
Item[] forge = TPlayer.bank3.item;
Item trash = TPlayer.trashItem;
for (int i = 0; i < NetItem.MaxInventory; i++)
{
if (i < NetItem.InventoryIndex.Item2)
{
// From above: this is slots 0-58 in the inventory.
// 0-58
Item item = new Item();
if (inventory[i] != null && inventory[i].netID != 0)
{
item.netDefaults(inventory[i].netID);
item.Prefix(inventory[i].prefix);
item.AffixName();
if (inventory[i].stack > item.maxStack || inventory[i].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, inventory[i].stack);
}
}
}
}
else if (i < NetItem.ArmorIndex.Item2)
{
// 59-78
var index = i - NetItem.ArmorIndex.Item1;
Item item = new Item();
if (armor[index] != null && armor[index].netID != 0)
{
item.netDefaults(armor[index].netID);
item.Prefix(armor[index].prefix);
item.AffixName();
if (armor[index].stack > item.maxStack || armor[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove armor {0} ({1}) and then rejoin.", item.Name, armor[index].stack);
}
}
}
}
else if (i < NetItem.DyeIndex.Item2)
{
// 79-88
var index = i - NetItem.DyeIndex.Item1;
Item item = new Item();
if (dye[index] != null && dye[index].netID != 0)
{
item.netDefaults(dye[index].netID);
item.Prefix(dye[index].prefix);
item.AffixName();
if (dye[index].stack > item.maxStack || dye[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove dye {0} ({1}) and then rejoin.", item.Name, dye[index].stack);
}
}
}
}
else if (i < NetItem.MiscEquipIndex.Item2)
{
// 89-93
var index = i - NetItem.MiscEquipIndex.Item1;
Item item = new Item();
if (miscEquips[index] != null && miscEquips[index].netID != 0)
{
item.netDefaults(miscEquips[index].netID);
item.Prefix(miscEquips[index].prefix);
item.AffixName();
if (miscEquips[index].stack > item.maxStack || miscEquips[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove item {0} ({1}) and then rejoin.", item.Name, miscEquips[index].stack);
}
}
}
}
else if (i < NetItem.MiscDyeIndex.Item2)
{
// 93-98
var index = i - NetItem.MiscDyeIndex.Item1;
Item item = new Item();
if (miscDyes[index] != null && miscDyes[index].netID != 0)
{
item.netDefaults(miscDyes[index].netID);
item.Prefix(miscDyes[index].prefix);
item.AffixName();
if (miscDyes[index].stack > item.maxStack || miscDyes[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove item dye {0} ({1}) and then rejoin.", item.Name, miscDyes[index].stack);
}
}
}
}
else if (i < NetItem.PiggyIndex.Item2)
{
// 98-138
var index = i - NetItem.PiggyIndex.Item1;
Item item = new Item();
if (piggy[index] != null && piggy[index].netID != 0)
{
item.netDefaults(piggy[index].netID);
item.Prefix(piggy[index].prefix);
item.AffixName();
if (piggy[index].stack > item.maxStack || piggy[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove piggy-bank item {0} ({1}) and then rejoin.", item.Name, piggy[index].stack);
}
}
}
}
else if (i < NetItem.SafeIndex.Item2)
{
// 138-178
var index = i - NetItem.SafeIndex.Item1;
Item item = new Item();
if (safe[index] != null && safe[index].netID != 0)
{
item.netDefaults(safe[index].netID);
item.Prefix(safe[index].prefix);
item.AffixName();
if (safe[index].stack > item.maxStack || safe[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove safe item {0} ({1}) and then rejoin.", item.Name, safe[index].stack);
}
}
}
}
else if (i < NetItem.TrashIndex.Item2)
{
// 179-219
Item item = new Item();
if (trash != null && trash.netID != 0)
{
item.netDefaults(trash.netID);
item.Prefix(trash.prefix);
item.AffixName();
if (trash.stack > item.maxStack)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove trash item {0} ({1}) and then rejoin.", item.Name, trash.stack);
}
}
}
}
else
{
// 220
var index = i - NetItem.ForgeIndex.Item1;
Item item = new Item();
if (forge[index] != null && forge[index].netID != 0)
{
item.netDefaults(forge[index].netID);
item.Prefix(forge[index].prefix);
item.AffixName();
if (forge[index].stack > item.maxStack || forge[index].stack < 0)
{
check = true;
if (shouldWarnPlayer)
{
SendErrorMessage("Stack cheat detected. Remove Defender's Forge item {0} ({1}) and then rejoin.", item.Name, forge[index].stack);
}
}
}
}
}
return check;
}
/// <summary>
/// The player's server side inventory data.
@ -299,30 +566,147 @@ namespace TShockAPI
public bool SilentJoinInProgress;
/// <summary>Checks if a player is in range of a given tile if range checks are enabled.</summary>
/// <param name="x"> The x coordinate of the tile.</param>
/// <param name="y">The y coordinate of the tile.</param>
/// <param name="range">The range to check for.</param>
/// <returns>True if the player is in range of a tile or if range checks are off. False if not.</returns>
public bool IsInRange(int x, int y, int range = 32)
{
if (TShock.Config.RangeChecks && ((Math.Abs(TileX - x) > range) || (Math.Abs(TileY - y) > range)))
{
return false;
}
return true;
}
private enum BuildPermissionFailPoint
{
GeneralBuild,
SpawnProtect,
Regions
}
/// <summary>Determines if the player can build on a given point.</summary>
/// <param name="x">The x coordinate they want to build at.</param>
/// <param name="y">The y coordinate they want to paint at.</param>
/// <returns>True if the player can build at the given point from build, spawn, and region protection.</returns>
public bool HasBuildPermission(int x, int y, bool shouldWarnPlayer = true)
{
BuildPermissionFailPoint failure = BuildPermissionFailPoint.GeneralBuild;
// The goal is to short circuit on easy stuff as much as possible.
// Don't compute permissions unless needed, and don't compute taxing stuff unless needed.
// If the player has bypass on build protection or building is enabled; continue
// (General build protection takes precedence over spawn protection)
if (!TShock.Config.DisableBuild || HasPermission(Permissions.antibuild))
{
failure = BuildPermissionFailPoint.SpawnProtect;
// If they have spawn protect bypass, or it isn't spawn, or it isn't in spawn; continue
// (If they have spawn protect bypass, we don't care if it's spawn or not)
if (!TShock.Config.SpawnProtection || HasPermission(Permissions.editspawn) || !Utils.IsInSpawn(x, y))
{
failure = BuildPermissionFailPoint.Regions;
// If they have build permission in this region, then they're allowed to continue
if (TShock.Regions.CanBuild(x, y, this))
{
return true;
}
}
}
// If they lack build permission, they end up here.
// If they have build permission but lack the ability to edit spawn and it's spawn, they end up here.
// If they have build, it isn't spawn, or they can edit spawn, but they fail the region check, they end up here.
// If they shouldn't be warned, exit early.
if (!shouldWarnPlayer)
return false;
// Space out warnings by 2 seconds so that they don't get spammed.
if (((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - lastPermissionWarning) < 2000)
{
return false;
}
// If they should be warned, warn them.
switch (failure)
{
case BuildPermissionFailPoint.GeneralBuild:
SendErrorMessage("You lack permission to build on this server.");
break;
case BuildPermissionFailPoint.SpawnProtect:
SendErrorMessage("You lack permission to build in the spawn point.");
break;
case BuildPermissionFailPoint.Regions:
SendErrorMessage("You lack permission to build in this region.");
break;
}
// Set the last warning time to now.
lastPermissionWarning = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
return false;
}
/// <summary>Determines if the player can paint on a given point. Checks general build permissions, then paint.</summary>
/// <param name="x">The x coordinate they want to paint at.</param>
/// <param name="y">The y coordinate they want to paint at.</param>
/// <returns>True if they can paint.</returns>
public bool HasPaintPermission(int x, int y)
{
return HasBuildPermission(x, y) || HasPermission(Permissions.canpaint);
}
/// <summary>Checks if a player can place ice, and if they can, tracks ice placements and removals.</summary>
/// <param name="x">The x coordinate of the suspected ice block.</param>
/// <param name="y">The y coordinate of the suspected ice block.</param>
/// <param name="tileType">The tile type of the suspected ice block.</param>
/// <param name="editAction">The EditAction on the suspected ice block.</param>
/// <returns>True if a player successfully places an ice tile or removes one of their past ice tiles.</returns>
public bool HasModifiedIceSuccessfully(int x, int y, short tileType, GetDataHandlers.EditAction editAction)
{
// The goal is to short circuit ASAP.
// A subsequent call to HasBuildPermission can figure this out if not explicitly ice.
if (!TShock.Config.AllowIce)
{
return false;
}
// They've placed some ice. Horrible!
if (editAction == GetDataHandlers.EditAction.PlaceTile && tileType == TileID.MagicalIceBlock)
{
IceTiles.Add(new Point(x, y));
return true;
}
// The edit wasn't an add, so we check to see if the position matches any of the known ice tiles
if (editAction == GetDataHandlers.EditAction.KillTile)
{
foreach (Point p in IceTiles)
{
// If they're trying to kill ice or dirt, and the tile was in the list, we allow it.
if (p.X == x && p.Y == y && (Main.tile[p.X, p.Y].type == TileID.Dirt || Main.tile[p.X, p.Y].type == TileID.MagicalIceBlock))
{
IceTiles.Remove(p);
return true;
}
}
}
// Only a small number of cases let this happen.
return false;
}
/// <summary>
/// A list of points where ice tiles have been placed.
/// </summary>
public List<Point> IceTiles;
/// <summary>
/// Unused, can be removed.
/// The last time the player was warned for build permissions.
/// In MS, defaults to 1 (so it will warn on the first attempt).
/// </summary>
public long RPm = 1;
/// <summary>
/// World protection message cool down.
/// </summary>
public long WPm = 1;
/// <summary>
/// Spawn protection message cool down.
/// </summary>
public long SPm = 1;
/// <summary>
/// Permission to build message cool down.
/// </summary>
public long BPm = 1;
public long lastPermissionWarning = 1;
/// <summary>
/// The time in ms when the player has logged in.
@ -375,7 +759,7 @@ namespace TShockAPI
get
{
return RealPlayer
&& (Netplay.Clients[Index] != null && Netplay.Clients[Index].IsActive && !Netplay.Clients[Index].PendingTermination);
&& (Netplay.Clients[Index] != null && Netplay.Clients[Index].IsActive && !Netplay.Clients[Index].PendingTermination);
}
}
@ -413,10 +797,10 @@ namespace TShockAPI
{
if (string.IsNullOrEmpty(CacheIP))
return
CacheIP = RealPlayer ? (Netplay.Clients[Index].Socket.IsConnected()
? TShock.Utils.GetRealIP(Netplay.Clients[Index].Socket.GetRemoteAddress().ToString())
: "")
: "";
CacheIP = RealPlayer ? (Netplay.Clients[Index].Socket.IsConnected()
? TShock.Utils.GetRealIP(Netplay.Clients[Index].Socket.GetRemoteAddress().ToString())
: "")
: "";
else
return CacheIP;
}
@ -448,7 +832,7 @@ namespace TShockAPI
{
if (HasPermission(Permissions.bypassssc))
{
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + User.Name); // Debug Code
TShock.Log.ConsoleInfo("Skipping SSC Backup for " + Account.Name); // Debug Code
return true;
}
PlayerData.CopyCharacter(this);
@ -522,7 +906,7 @@ namespace TShockAPI
/// </summary>
public float X
{
get { return RealPlayer ? TPlayer.position.X : Main.spawnTileX*16; }
get { return RealPlayer ? TPlayer.position.X : Main.spawnTileX * 16; }
}
/// <summary>
@ -530,7 +914,7 @@ namespace TShockAPI
/// </summary>
public float Y
{
get { return RealPlayer ? TPlayer.position.Y : Main.spawnTileY*16; }
get { return RealPlayer ? TPlayer.position.Y : Main.spawnTileY * 16; }
}
/// <summary>
@ -538,7 +922,7 @@ namespace TShockAPI
/// </summary>
public int TileX
{
get { return (int) (X/16); }
get { return (int)(X / 16); }
}
/// <summary>
@ -546,14 +930,9 @@ namespace TShockAPI
/// </summary>
public int TileY
{
get { return (int) (Y/16); }
get { return (int)(Y / 16); }
}
/// <summary>
/// Unused.
/// </summary>
public bool TpLock;
/// <summary>
/// Checks if the player has any inventory slots available.
/// </summary>
@ -646,8 +1025,8 @@ namespace TShockAPI
PlayerHooks.OnPlayerLogout(this);
if (Main.ServerSideCharacter)
{
IgnoreActionsForInventory = $"Server side characters is enabled! Please {Commands.Specifier}register or {Commands.Specifier}login to play!";
if (!IgnoreActionsForClearingTrashCan && (!Dead || TPlayer.difficulty != 2))
IsDisabledForSSC = true;
if (!IsDisabledPendingTrashRemoval && (!Dead || TPlayer.difficulty != 2))
{
PlayerData.CopyCharacter(this);
TShock.CharacterDB.InsertPlayerData(this);
@ -661,7 +1040,7 @@ namespace TShockAPI
{
tempGroupTimer.Stop();
}
User = null;
Account = null;
IsLoggedIn = false;
}
@ -688,7 +1067,7 @@ namespace TShockAPI
TilesDestroyed = new Dictionary<Vector2, ITile>();
TilesCreated = new Dictionary<Vector2, ITile>();
Index = -1;
FakePlayer = new Player {name = playerName, whoAmI = -1};
FakePlayer = new Player { name = playerName, whoAmI = -1 };
Group = Group.DefaultGroup;
AwaitingResponse = new Dictionary<string, Action<object>>();
}
@ -744,7 +1123,7 @@ namespace TShockAPI
y = 992;
}
SendTileSquare((int) (x/16), (int) (y/16), 15);
SendTileSquare((int)(x / 16), (int)(y / 16), 15);
TPlayer.Teleport(new Vector2(x, y), style);
NetMessage.SendData((int)PacketTypes.Teleport, -1, -1, NetworkText.Empty, 0, TPlayer.whoAmI, x, y, style);
return true;
@ -784,11 +1163,11 @@ namespace TShockAPI
using (var ms = new MemoryStream())
{
var msg = new SpawnMsg
{
PlayerIndex = (byte) Index,
TileX = (short)tilex,
TileY = (short)tiley
};
{
PlayerIndex = (byte)Index,
TileX = (short)tilex,
TileY = (short)tiley
};
msg.PackFull(ms);
SendRawData(ms.ToArray());
}
@ -804,20 +1183,29 @@ namespace TShockAPI
using (var ms = new MemoryStream())
{
var msg = new ProjectileRemoveMsg
{
Index = (short) index,
Owner = (byte) owner
};
{
Index = (short)index,
Owner = (byte)owner
};
msg.PackFull(ms);
SendRawData(ms.ToArray());
}
}
/// <summary>Sends a tile square at a location with a given size.
/// Typically used to revert changes by Bouncer through sending the
/// "old" version of modified data back to a client.
/// Prevents desync issues.
/// </summary>
/// <param name="x">The x coordinate to send.</param>
/// <param name="y">The y coordinate to send.</param>
/// <param name="size">The size square set of tiles to send.</param>
/// <returns>Status if the tile square was sent successfully (i.e. no exceptions).</returns>
public virtual bool SendTileSquare(int x, int y, int size = 10)
{
try
{
int num = (size - 1)/2;
int num = (size - 1) / 2;
int m_x = 0;
int m_y = 0;
@ -866,48 +1254,31 @@ namespace TShockAPI
/// <summary>
/// Gives an item to the player. Includes banned item spawn prevention to check if the player can spawn the item.
/// </summary>
/// <param name="type"></param>
/// <param name="name"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="stack"></param>
/// <param name="prefix"></param>
/// <param name="type">The item ID.</param>
/// <param name="name">The item name.</param>
/// <param name="stack">The item stack.</param>
/// <param name="prefix">The item prefix.</param>
/// <returns>True or false, depending if the item passed the check or not.</returns>
public bool GiveItemCheck(int type, string name, int width, int height, int stack, int prefix = 0)
public bool GiveItemCheck(int type, string name, int stack, int prefix = 0)
{
if ((TShock.Itembans.ItemIsBanned(name) && TShock.Config.PreventBannedItemSpawn) &&
(TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems))
return false;
(TShock.Itembans.ItemIsBanned(name, this) || !TShock.Config.AllowAllowedGroupsToSpawnBannedItems))
return false;
GiveItem(type, name, width, height, stack, prefix);
GiveItem(type, stack, prefix);
return true;
}
/// <summary>
/// Gives an item to the player.
/// </summary>
/// <param name="type">The item's netID.</param>
/// <param name="name">The tiem's name.</param>
/// <param name="width">The item's width.</param>
/// <param name="height">The item's height.</param>
/// <param name="stack">The item's stack.</param>
/// <param name="prefix">The item's prefix.</param>
public virtual void GiveItem(int type, string name, int width, int height, int stack, int prefix = 0)
/// <param name="type">The item ID.</param>
/// <param name="stack">The item stack.</param>
/// <param name="prefix">The item prefix.</param>
public virtual void GiveItem(int type, int stack, int prefix = 0)
{
int itemid = Item.NewItem((int) X, (int) Y, width, height, type, stack, true, prefix, true);
// This is for special pickaxe/hammers/swords etc
Main.item[itemid].netDefaults(type);
// The set default overrides the wet and stack set by NewItem
Main.item[itemid].wet = Collision.WetCollision(Main.item[itemid].position, Main.item[itemid].width,
Main.item[itemid].height);
Main.item[itemid].stack = stack;
Main.item[itemid].owner = Index;
Main.item[itemid].prefix = (byte) prefix;
Main.item[itemid].noGrabDelay = 1;
Main.item[itemid].velocity = Main.player[this.Index].velocity;
NetMessage.SendData((int)PacketTypes.ItemDrop, -1, -1, NetworkText.Empty, itemid, 0f, 0f, 0f);
NetMessage.SendData((int)PacketTypes.ItemOwner, -1, -1, NetworkText.Empty, itemid, 0f, 0f, 0f);
int itemIndex = Item.NewItem((int)X, (int)Y, TPlayer.width, TPlayer.height, type, stack, true, prefix, true);
SendData(PacketTypes.ItemDrop, "", itemIndex);
}
/// <summary>
@ -1043,6 +1414,43 @@ namespace TShockAPI
SendDataFromPlayer(PacketTypes.SmartTextMessage, ply, msg, red, green, blue, -1);
}
/// <summary>
/// Sends the text of a given file to the player. Replacement of %map% and %players% if in the file.
/// </summary>
/// <param name="file">Filename relative to <see cref="TShock.SavePath"></see></param>
public void SendFileTextAsMessage(string file)
{
string foo = "";
bool containsOldFormat = false;
using (var tr = new StreamReader(file))
{
Color lineColor;
while ((foo = tr.ReadLine()) != null)
{
lineColor = Color.White;
if (string.IsNullOrWhiteSpace(foo))
{
continue;
}
var players = new List<string>();
foreach (TSPlayer ply in TShock.Players)
{
if (ply != null && ply.Active)
{
players.Add(ply.Name);
}
}
foo = foo.Replace("%map%", (TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName));
foo = foo.Replace("%players%", String.Join(",", players));
SendMessage(foo, lineColor);
}
}
}
/// <summary>
/// Wounds the player with the given damage.
/// </summary>
@ -1068,7 +1476,6 @@ namespace TShockAPI
{
Main.player[Index].team = team;
NetMessage.SendData((int)PacketTypes.PlayerTeam, -1, -1, NetworkText.Empty, Index);
NetMessage.SendData((int)PacketTypes.PlayerTeam, -1, Index, NetworkText.Empty, Index);
}
private DateTime LastDisableNotification = DateTime.UtcNow;
@ -1128,6 +1535,79 @@ namespace TShockAPI
LogStackFrame();
}
/// <summary>
/// Disconnects this player from the server with a reason.
/// </summary>
/// <param name="reason">The reason to display to the user and to the server on kick.</param>
/// <param name="force">If the kick should happen regardless of immunity to kick permissions.</param>
/// <param name="silent">If no message should be broadcasted to the server.</param>
/// <param name="adminUserName">The originator of the kick, for display purposes.</param>
/// <param name="saveSSI">If the player's server side character should be saved on kick.</param>
public bool Kick(string reason, bool force = false, bool silent = false, string adminUserName = null, bool saveSSI = false)
{
if (!ConnectionAlive)
return true;
if (force || !HasPermission(Permissions.immunetokick))
{
SilentKickInProgress = silent;
if (IsLoggedIn && saveSSI)
SaveServerCharacter();
Disconnect(string.Format("Kicked: {0}", reason));
TShock.Log.ConsoleInfo(string.Format("Kicked {0} for : '{1}'", Name, reason));
string verb = force ? "force " : "";
if (!silent)
{
if (string.IsNullOrWhiteSpace(adminUserName))
TShock.Utils.Broadcast(string.Format("{0} was {1}kicked for '{2}'", Name, verb, reason.ToLower()), Color.Green);
else
TShock.Utils.Broadcast(string.Format("{0} {1}kicked {2} for '{3}'", adminUserName, verb, Name, reason.ToLower()), Color.Green);
}
return true;
}
return false;
}
/// <summary>
/// Bans and disconnects the player from the server.
/// </summary>
/// <param name="reason">The reason to be displayed to the server.</param>
/// <param name="force">If the ban should bypass immunity to ban checks.</param>
/// <param name="adminUserName">The player who initiated the ban.</param>
public bool Ban(string reason, bool force = false, string adminUserName = null)
{
if (!ConnectionAlive)
return true;
if (force || !HasPermission(Permissions.immunetoban))
{
string ip = IP;
string uuid = UUID;
TShock.Bans.AddBan(ip, Name, uuid, "", reason, false, adminUserName);
Disconnect(string.Format("Banned: {0}", reason));
string verb = force ? "force " : "";
if (string.IsNullOrWhiteSpace(adminUserName))
TSPlayer.All.SendInfoMessage("{0} was {1}banned for '{2}'.", Name, verb, reason);
else
TSPlayer.All.SendInfoMessage("{0} {1}banned {2} for '{3}'.", adminUserName, verb, Name, reason);
return true;
}
return false;
}
/// <summary>
/// Sends the player an error message stating that more than one match was found
/// appending a csv list of the matches.
/// </summary>
/// <param name="matches">An enumerable list with the matches</param>
public void SendMultipleMatchError(IEnumerable<object> matches)
{
SendErrorMessage("More than one match found: ");
var lines = PaginationTools.BuildLinesFromTerms(matches.ToArray());
lines.ForEach(SendInfoMessage);
SendErrorMessage("Use \"my query\" for items with spaces.");
}
[Conditional("DEBUG")]
private void LogStackFrame()
{
@ -1144,7 +1624,7 @@ namespace TShockAPI
/// <param name="time">The</param>
public virtual void Whoopie(object time)
{
var time2 = (int) time;
var time2 = (int)time;
var launch = DateTime.UtcNow;
var startname = Name;
SendInfoMessage("You are now being annoyed.");
@ -1186,7 +1666,7 @@ namespace TShockAPI
if (RealPlayer && !ConnectionAlive)
return;
NetMessage.SendData((int) msgType, Index, -1, NetworkText.FromLiteral(text), number, number2, number3, number4, number5);
NetMessage.SendData((int)msgType, Index, -1, NetworkText.FromLiteral(text), number, number2, number3, number4, number5);
}
/// <summary>
@ -1205,7 +1685,7 @@ namespace TShockAPI
if (RealPlayer && !ConnectionAlive)
return;
NetMessage.SendData((int) msgType, Index, -1, NetworkText.FromFormattable(text), ply, number2, number3, number4, number5);
NetMessage.SendData((int)msgType, Index, -1, NetworkText.FromFormattable(text), ply, number2, number3, number4, number5);
}
/// <summary>
@ -1225,9 +1705,9 @@ namespace TShockAPI
/// </summary>
/// <param name="name">The string representing the command i.e "yes" == /yes</param>
/// <param name="callback">The method that will be executed on confirmation ie user accepts</param>
public void AddResponse( string name, Action<object> callback)
public void AddResponse(string name, Action<object> callback)
{
if( AwaitingResponse.ContainsKey(name))
if (AwaitingResponse.ContainsKey(name))
{
AwaitingResponse.Remove(name);
}
@ -1245,21 +1725,55 @@ namespace TShockAPI
/// <returns>True if the player has that permission.</returns>
public bool HasPermission(string permission)
{
if (PlayerHooks.OnPlayerPermission(this, permission))
return true;
PermissionHookResult hookResult = PlayerHooks.OnPlayerPermission(this, permission);
if (hookResult != PermissionHookResult.Unhandled)
return hookResult == PermissionHookResult.Granted;
if (tempGroup != null)
return tempGroup.HasPermission(permission);
else
return Group.HasPermission(permission);
}
/// <summary>
/// Checks to see if a player has permission to use the specific banned item.
/// Fires the <see cref="PlayerHooks.OnPlayerItembanPermission"/> hook which may be handled to override item ban permission checks.
/// </summary>
/// <param name="bannedItem">The <see cref="ItemBan" /> to check.</param>
/// <returns>True if the player has permission to use the banned item.</returns>
public bool HasPermission(ItemBan bannedItem)
{
return TShock.Itembans.ItemIsBanned(bannedItem.Name, this);
}
/// <summary>
/// Checks to see if a player has permission to use the specific banned projectile.
/// Fires the <see cref="PlayerHooks.OnPlayerProjbanPermission"/> hook which may be handled to override projectile ban permission checks.
/// </summary>
/// <param name="bannedProj">The <see cref="ProjectileBan" /> to check.</param>
/// <returns>True if the player has permission to use the banned projectile.</returns>
public bool HasPermission(ProjectileBan bannedProj)
{
return TShock.ProjectileBans.ProjectileIsBanned(bannedProj.ID, this);
}
/// <summary>
/// Checks to see if a player has permission to use the specific banned tile.
/// Fires the <see cref="PlayerHooks.OnPlayerTilebanPermission"/> hook which may be handled to override tile ban permission checks.
/// </summary>
/// <param name="bannedTile">The <see cref="TileBan" /> to check.</param>
/// <returns>True if the player has permission to use the banned tile.</returns>
public bool HasPermission(TileBan bannedTile)
{
return TShock.TileBans.TileIsBanned(bannedTile.ID, this);
}
}
public class TSRestPlayer : TSPlayer
{
internal List<string> CommandOutput = new List<string>();
public TSRestPlayer(string playerName, Group playerGroup): base(playerName)
public TSRestPlayer(string playerName, Group playerGroup) : base(playerName)
{
Group = playerGroup;
AwaitingResponse = new Dictionary<string, Action<object>>();

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -36,7 +36,7 @@ namespace TShockAPI
: base("Server")
{
Group = new SuperAdminGroup();
User = new User { Name = AccountName };
Account = new UserAccount { Name = AccountName };
}
public override void SendErrorMessage(string msg)
@ -152,12 +152,7 @@ namespace TShockAPI
int spawnTileY;
TShock.Utils.GetRandomClearTileWithInRange(startTileX, startTileY, tileXRange, tileYRange, out spawnTileX,
out spawnTileY);
int npcid = NPC.NewNPC(spawnTileX * 16, spawnTileY * 16, type, 0);
// TODO: If special slimes break look at the git blame for this spot
// It's probably because I removed something that didn't work
Main.npc[npcid].SetDefaults(type);
NPC.NewNPC(spawnTileX * 16, spawnTileY * 16, type);
}
}

File diff suppressed because it is too large Load diff

View file

@ -39,7 +39,6 @@
<DocumentationFile>bin\Debug\TShockAPI.XML</DocumentationFile>
<PlatformTarget>x86</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
<!-- <NoWarn>1591</NoWarn> -->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -71,9 +70,9 @@
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="OTAPI, Version=1.3.4.4, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="OTAPI=">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\TerrariaServerAPI\TerrariaServerAPI\bin\Debug\OTAPI.dll</HintPath>
<HintPath Condition="Exists('..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll')">..\TerrariaServerAPI\TerrariaServerAPI\bin\$(ConfigurationName)\OTAPI.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
@ -96,6 +95,7 @@
<Compile Include="Localization\EnglishLanguage.cs" />
<Compile Include="NetItem.cs" />
<Compile Include="PlayerData.cs" />
<Compile Include="RegionHandler.cs" />
<Compile Include="Sockets\LinuxTcpSocket.cs" />
<Compile Include="SqlLog.cs" />
<Compile Include="TextLog.cs" />
@ -131,6 +131,8 @@
<Compile Include="Net\WorldInfoMsg.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="DB\RememberedPosManager.cs" />
<Compile Include="Bouncer.cs" />
<Compile Include="ItemBans.cs" />
<Compile Include="Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -191,6 +193,7 @@
<Name>TerrariaServerAPI</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>

View file

@ -1,7 +1,7 @@
extensions: .cs
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ namespace TShockAPI
/// </summary>
public class UpdateManager
{
private const string UpdateUrl = "http://update.tshock.co/latest/";
private const string UpdateUrl = "https://update.tshock.co/latest/";
private HttpClient _client = new HttpClient();
/// <summary>

View file

@ -1,6 +1,6 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2017 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -52,12 +52,6 @@ namespace TShockAPI
/// <summary>instance - an instance of the utils class</summary>
private static readonly Utils instance = new Utils();
/// <summary> This regex will look for the old MotD format for colors and replace them with the new chat format. </summary>
private Regex motdColorRegex = new Regex(@"\%\s*(?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*\%(?<text>((?!(\%\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\%)|(\[[a-zA-Z]/[^:]+:[^\]]*\])).)*)");
/// <summary> Matches the start of a line with our legacy color format</summary>
private Regex startOfLineColorRegex = new Regex(@"^\%\s*(?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*\%");
/// <summary>Utils - Creates a utilities object.</summary>
private Utils() {}
@ -75,52 +69,6 @@ namespace TShockAPI
return mess.Split(':')[0];
}
/// <summary>
/// Returns a list of current players on the server
/// </summary>
/// <param name="includeIDs">bool includeIDs - whether or not the string of each player name should include ID data</param>
/// <returns>List of strings with names</returns>
public List<string> GetPlayers(bool includeIDs)
{
var players = new List<string>();
foreach (TSPlayer ply in TShock.Players)
{
if (ply != null && ply.Active)
{
if (includeIDs)
{
players.Add(String.Format("{0} (IX: {1}{2})", ply.Name, ply.Index, ply.User != null ? ", ID: " + ply.User.ID : ""));
}
else
{
players.Add(ply.Name);
}
}
}
return players;
}
/// <summary>
/// Finds a player and gets IP as string
/// </summary>
/// <param name="playername">string playername</param>
public string GetPlayerIP(string playername)
{
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
{
if (playername.ToLower() == player.Name.ToLower())
{
return player.IP;
}
}
}
return null;
}
/// <summary>
/// It's a clamp function
/// </summary>
@ -205,48 +153,11 @@ namespace TShockAPI
/// Gets the number of active players on the server.
/// </summary>
/// <returns>The number of active players on the server.</returns>
public int ActivePlayers()
public int GetActivePlayerCount()
{
return Main.player.Where(p => null != p && p.active).Count();
}
/// <summary>
/// Finds a TSPlayer based on name or ID
/// </summary>
/// <param name="plr">Player name or ID</param>
/// <returns>A list of matching players</returns>
public List<TSPlayer> FindPlayer(string plr)
{
var found = new List<TSPlayer>();
// Avoid errors caused by null search
if (plr == null)
return found;
byte plrID;
if (byte.TryParse(plr, out plrID) && plrID < Main.maxPlayers)
{
TSPlayer player = TShock.Players[plrID];
if (player != null && player.Active)
{
return new List<TSPlayer> { player };
}
}
string plrLower = plr.ToLower();
foreach (TSPlayer player in TShock.Players)
{
if (player != null)
{
// Must be an EXACT match
if (player.Name == plr)
return new List<TSPlayer> { player };
if (player.Name.ToLower().StartsWith(plrLower))
found.Add(player);
}
}
return found;
}
//Random should not be generated in a method
Random r = new Random();
@ -455,7 +366,7 @@ namespace TShockAPI
/// <returns>name</returns>
public string GetBuffName(int id)
{
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffName(id) : "null";
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffName(id) : null;
}
/// <summary>
@ -465,7 +376,7 @@ namespace TShockAPI
/// <returns>description</returns>
public string GetBuffDescription(int id)
{
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffName(id) : "null";
return (id > 0 && id < Main.maxBuffTypes) ? Lang.GetBuffDescription(id) : null;
}
/// <summary>
@ -542,21 +453,6 @@ namespace TShockAPI
return GetPrefixByName(idOrName);
}
/// <summary>
/// Kicks all player from the server without checking for immunetokick permission.
/// </summary>
/// <param name="reason">string reason</param>
public void ForceKickAll(string reason)
{
foreach (TSPlayer player in TShock.Players)
{
if (player != null && player.Active)
{
ForceKick(player, reason, false, true);
}
}
}
/// <summary>
/// Stops the server after kicking all players with a reason message, and optionally saving the world
/// </summary>
@ -566,12 +462,10 @@ namespace TShockAPI
{
TShock.ShuttingDown = true;
ForceKickAll(reason);
if (save)
SaveManager.Instance.SaveWorld();
// Save takes a while so kick again
ForceKickAll(reason);
TSPlayer.All.Kick(reason, true, true, null, true);
// Broadcast so console can see we are shutting down as well
TShock.Utils.Broadcast(reason, Color.Red);
@ -580,28 +474,10 @@ namespace TShockAPI
Netplay.disconnect = true;
}
/// <summary>
/// Stops the server after kicking all players with a reason message, and optionally saving the world then attempts to
/// restart it.
/// </summary>
/// <param name="save">bool perform a world save before stop (default: true)</param>
/// <param name="reason">string reason (default: "Server shutting down!")</param>
public void RestartServer(bool save = true, string reason = "Server shutting down!")
{
if (Main.ServerSideCharacter)
foreach (TSPlayer player in TShock.Players)
if (player != null && player.IsLoggedIn && !player.IgnoreActionsForClearingTrashCan)
TShock.CharacterDB.InsertPlayerData(player);
StopServer(true, reason);
System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
Environment.Exit(0);
}
/// <summary>
/// Reloads all configuration settings, groups, regions and raises the reload event.
/// </summary>
public void Reload(TSPlayer player)
public void Reload()
{
FileTools.SetupConfig();
TShock.HandleCommandLinePostConfigLoad(Environment.GetCommandLineArgs());
@ -609,244 +485,14 @@ namespace TShockAPI
TShock.Regions.Reload();
TShock.Itembans.UpdateItemBans();
TShock.ProjectileBans.UpdateBans();
TShock.TileBans.UpdateBans();
Hooks.GeneralHooks.OnReloadEvent(player);
}
/// <summary>
/// Kicks a player from the server without checking for immunetokick permission.
/// </summary>
/// <param name="player">TSPlayer player</param>
/// <param name="reason">string reason</param>
/// <param name="silent">bool silent (default: false)</param>
/// <param name="saveSSI">bool saveSSI (default: false)</param>
public void ForceKick(TSPlayer player, string reason, bool silent = false, bool saveSSI = false)
{
Kick(player, reason, true, silent, null, saveSSI);
}
/// <summary>
/// Kicks a player from the server..
/// </summary>
/// <param name="player">TSPlayer player</param>
/// <param name="reason">string reason</param>
/// <param name="force">bool force (default: false)</param>
/// <param name="silent">bool silent (default: false)</param>
/// <param name="adminUserName">string adminUserName (default: null)</param>
/// <param name="saveSSI">bool saveSSI (default: false)</param>
public bool Kick(TSPlayer player, string reason, bool force = false, bool silent = false, string adminUserName = null, bool saveSSI = false)
{
if (!player.ConnectionAlive)
return true;
if (force || !player.HasPermission(Permissions.immunetokick))
{
string playerName = player.Name;
player.SilentKickInProgress = silent;
if (player.IsLoggedIn && saveSSI)
player.SaveServerCharacter();
player.Disconnect(string.Format("Kicked: {0}", reason));
TShock.Log.ConsoleInfo(string.Format("Kicked {0} for : '{1}'", playerName, reason));
string verb = force ? "force " : "";
if (!silent)
{
if (string.IsNullOrWhiteSpace(adminUserName))
Broadcast(string.Format("{0} was {1}kicked for '{2}'", playerName, verb, reason.ToLower()), Color.Green);
else
Broadcast(string.Format("{0} {1}kicked {2} for '{3}'", adminUserName, verb, playerName, reason.ToLower()), Color.Green);
}
return true;
}
return false;
}
/// <summary>
/// Bans and kicks a player from the server.
/// </summary>
/// <param name="player">TSPlayer player</param>
/// <param name="reason">string reason</param>
/// <param name="force">bool force (default: false)</param>
/// <param name="adminUserName">string adminUserName (default: null)</param>
public bool Ban(TSPlayer player, string reason, bool force = false, string adminUserName = null)
{
if (!player.ConnectionAlive)
return true;
if (force || !player.HasPermission(Permissions.immunetoban))
{
string ip = player.IP;
string uuid = player.UUID;
string playerName = player.Name;
TShock.Bans.AddBan(ip, playerName, uuid, reason, false, adminUserName);
player.Disconnect(string.Format("Banned: {0}", reason));
string verb = force ? "force " : "";
if (string.IsNullOrWhiteSpace(adminUserName))
TSPlayer.All.SendInfoMessage("{0} was {1}banned for '{2}'.", playerName, verb, reason);
else
TSPlayer.All.SendInfoMessage("{0} {1}banned {2} for '{3}'.", adminUserName, verb, playerName, reason);
return true;
}
return false;
}
/// <summary>HasBanExpired - Returns whether or not a ban has expired or not.</summary>
/// <param name="ban">ban - The ban object to check.</param>
/// <param name="byName">byName - Defines whether or not the ban should be checked by name.</param>
/// <returns>bool - True if the ban has expired.</returns>
public bool HasBanExpired(Ban ban, bool byName = false)
{
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (ban.ExpirationDateTime != null) && (DateTime.UtcNow >= ban.ExpirationDateTime))
{
if (byName)
{
TShock.Bans.RemoveBan(ban.Name, true, true, false);
}
else
{
TShock.Bans.RemoveBan(ban.IP, false, false, false);
}
return true;
}
return false;
}
/// <summary>
/// Shows a file to the user.
/// </summary>
/// <param name="player">Player the file contents will be sent to</param>
/// <param name="file">Filename relative to <see cref="TShock.SavePath"></see></param>
public void ShowFileToUser(TSPlayer player, string file)
{
string foo = "";
bool containsOldFormat = false;
using (var tr = new StreamReader(file))
{
Color lineColor;
while ((foo = tr.ReadLine()) != null)
{
lineColor = Color.White;
if (string.IsNullOrWhiteSpace(foo))
{
continue;
}
foo = foo.Replace("%map%", (TShock.Config.UseServerName ? TShock.Config.ServerName : Main.worldName));
foo = foo.Replace("%players%", String.Join(",", GetPlayers(false)));
var legacyColorMatch = startOfLineColorRegex.Match(foo);
if (legacyColorMatch.Success)
{
lineColor = new Color(Int32.Parse(legacyColorMatch.Groups["r"].Value),
Int32.Parse(legacyColorMatch.Groups["g"].Value),
Int32.Parse(legacyColorMatch.Groups["b"].Value));
foo = foo.Replace(legacyColorMatch.Groups[0].Value, "");
}
bool upgraded = false;
string newFoo = ReplaceDeprecatedColorCodes(foo, out upgraded);
if (upgraded && !containsOldFormat)
{
TShock.Log.ConsoleInfo($"You are using an old color format in file {file}.");
TShock.Log.ConsoleInfo("To send coloured text please use Terraria's inbuilt format of: [c/#hex:text].");
TShock.Log.ConsoleInfo("For example: [c/ff00aa:This is a message!].");
containsOldFormat = true;
}
foo = newFoo;
player.SendMessage(foo, lineColor);
}
}
}
/// <summary>
/// Returns a string with deprecated %###,###,###% formats replaced with the new chat format colors.
/// </summary>
/// <param name="input">The input string</param>
/// <param name="upgradedFormat">An out parameter that denotes if this line of text was upgraded.</param>
/// <returns>A replaced version of the input with the new chat color format.</returns>
private string ReplaceDeprecatedColorCodes(string input, out bool upgradedFormat)
{
String tempString = input;
Match match = null;
bool uFormat = false;
while ((match = motdColorRegex.Match(tempString)).Success)
{
uFormat = true;
tempString = tempString.Replace(match.Groups[0].Value, String.Format("[c/{0:X2}{1:X2}{2:X2}:{3}]", Int32.Parse(match.Groups["r"].Value), Int32.Parse(match.Groups["g"].Value), Int32.Parse(match.Groups["b"].Value), match.Groups["text"]));
}
upgradedFormat = uFormat;
return tempString;
}
/// <summary>
/// Upgrades a legacy MotD file to the new terraria chat tags version.
/// </summary>
public void UpgradeMotD()
{
string foo = "";
StringBuilder motd = new StringBuilder();
bool informedOwner = false;
using (var tr = new StreamReader(FileTools.MotdPath))
{
Color lineColor;
while ((foo = tr.ReadLine()) != null)
{
lineColor = Color.White;
var legacyColorMatch = startOfLineColorRegex.Match(foo);
if (legacyColorMatch.Success)
{
lineColor = new Color(Int32.Parse(legacyColorMatch.Groups["r"].Value),
Int32.Parse(legacyColorMatch.Groups["g"].Value),
Int32.Parse(legacyColorMatch.Groups["b"].Value));
foo = foo.Replace(legacyColorMatch.Groups[0].Value, "");
}
bool upgraded = false;
string newFoo = ReplaceDeprecatedColorCodes(foo, out upgraded);
if (!informedOwner && upgraded)
{
informedOwner = true;
TShock.Log.ConsoleInfo("We have upgraded your MotD to the new format. A backup has been created.");
}
if (lineColor != Color.White)
motd.Append(String.Format("%{0:d3},{1:d3},{2:d3}%", lineColor.R, lineColor.G, lineColor.B));
motd.AppendLine(newFoo);
}
}
if (informedOwner)
{
File.Copy(FileTools.MotdPath, String.Format("{0}_{1}.backup", FileTools.MotdPath, DateTime.Now.ToString("ddMMMyy_hhmmss")));
File.WriteAllText(FileTools.MotdPath, motd.ToString());
}
}
/// <summary>
/// Returns a Group from the name of the group
/// </summary>
/// <param name="groupName">string groupName</param>
public Group GetGroup(string groupName)
{
//first attempt on cached groups
for (int i = 0; i < TShock.Groups.groups.Count; i++)
{
if (TShock.Groups.groups[i].Name.Equals(groupName))
{
return TShock.Groups.groups[i];
}
}
return Group.DefaultGroup;
TShock.TileBans.UpdateBans();
}
/// <summary>
/// Returns an IPv4 address from a DNS query
/// </summary>
/// <param name="hostname">string ip</param>
public string GetIPv4Address(string hostname)
public string GetIPv4AddressFromHostname(string hostname)
{
try
{
@ -861,28 +507,11 @@ namespace TShockAPI
return "";
}
/// <summary>
/// Sends the player an error message stating that more than one match was found
/// appending a csv list of the matches.
/// </summary>
/// <param name="ply">Player to send the message to</param>
/// <param name="matches">An enumerable list with the matches</param>
public void SendMultipleMatchError(TSPlayer ply, IEnumerable<object> matches)
{
ply.SendErrorMessage("More than one match found: ");
var lines = PaginationTools.BuildLinesFromTerms(matches.ToArray());
lines.ForEach(ply.SendInfoMessage);
ply.SendErrorMessage("Use \"my query\" for items with spaces.");
}
/// <summary>
/// Checks if world has hit the max number of chests
/// </summary>
/// <returns>True if the entire chest array is used</returns>
public bool MaxChests()
public bool HasWorldReachedMaxChests()
{
for (int i = 0; i < Main.chest.Length; i++)
{
@ -905,7 +534,7 @@ namespace TShockAPI
var sb = new StringBuilder(3);
for (int i = 0; i < str.Length; i++)
{
if (Char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+'))
if (Char.IsDigit(str[i]) || (str[i] == '-' || str[i] == '+' || str[i] == ' '))
sb.Append(str[i]);
else
{
@ -1205,13 +834,14 @@ namespace TShockAPI
ServerSideCharacters.ServerSideConfig.DumpDescriptions();
RestManager.DumpDescriptions();
DumpBuffs("BuffList.txt");
DumpItems("Items-1_0.txt", -48, 235);
DumpItems("Items-1_0.txt", 1, 235);
DumpItems("Items-1_1.txt", 235, 604);
DumpItems("Items-1_2.txt", 604, 2749);
DumpItems("Items-1_3.txt", 2749, Main.maxItemTypes);
DumpNPCs("NPCs.txt");
DumpProjectiles("Projectiles.txt");
DumpPrefixes("Prefixes.txt");
if (exit)
{
Environment.Exit(1);
@ -1223,6 +853,52 @@ namespace TShockAPI
for(int i = 0; i < Main.recipe.Length; i++)
Main.recipe[i] = new Recipe();
}
/// <summary>Dumps a matrix of all permissions & all groups in Markdown table format.</summary>
/// <param name="path">The save destination.</param>
internal void DumpPermissionMatrix(string path)
{
StringBuilder output = new StringBuilder();
output.Append("|Permission|");
// Traverse to build group name list
foreach (Group g in TShock.Groups.groups)
{
output.Append(g.Name);
output.Append("|");
}
output.AppendLine();
output.Append("|-------|");
foreach (Group g in TShock.Groups.groups)
{
output.Append("-------|");
}
output.AppendLine();
foreach (var field in typeof(Permissions).GetFields().OrderBy(f => f.Name))
{
output.Append("|");
output.Append((string) field.GetValue(null));
output.Append("|");
foreach (Group g in TShock.Groups.groups)
{
if (g.HasPermission((string) field.GetValue(null)))
{
output.Append("✔|");
}
else
{
output.Append("|");
}
}
output.AppendLine();
}
File.WriteAllText(path, output.ToString());
}
public void DumpBuffs(string path)
{
@ -1427,5 +1103,104 @@ namespace TShockAPI
}
}
}
/// <summary>Starts an invasion on the server.</summary>
/// <param name="type">The invasion type id.</param>
internal void StartInvasion(int type)
{
int invasionSize = 0;
if (TShock.Config.InfiniteInvasion)
{
// Not really an infinite size
invasionSize = 20000000;
}
else
{
invasionSize = 100 + (TShock.Config.InvasionMultiplier * GetActivePlayerCount());
}
// Order matters
// StartInvasion will reset the invasion size
Main.StartInvasion(type);
// Note: This is a workaround to previously providing the size as a parameter in StartInvasion
// Have to set start size to report progress correctly
Main.invasionSizeStart = invasionSize;
Main.invasionSize = invasionSize;
}
/// <summary>Verifies that each stack in each chest is valid and not over the max stack count.</summary>
internal void FixChestStacks()
{
if (TShock.Config.IgnoreChestStacksOnLoad)
return;
foreach (Chest chest in Main.chest)
{
if (chest != null)
{
foreach (Item item in chest.item)
{
if (item != null && item.stack > item.maxStack)
item.stack = item.maxStack;
}
}
}
}
/// <summary>Updates the console title with some pertinent information.</summary>
/// <param name="empty">If the server is empty; determines if we should use Utils.GetActivePlayerCount() for player count or 0.</param>
internal void SetConsoleTitle(bool empty)
{
Console.Title = string.Format("{0}{1}/{2} on {3} @ {4}:{5} (TShock for Terraria v{6})",
!string.IsNullOrWhiteSpace(TShock.Config.ServerName) ? TShock.Config.ServerName + " - " : "",
empty ? 0 : GetActivePlayerCount(),
TShock.Config.MaxSlots, Main.worldName, Netplay.ServerIP.ToString(), Netplay.ListenPort, TShock.VersionNum);
}
/// <summary>Determines the distance between two vectors.</summary>
/// <param name="value1">The first vector location.</param>
/// <param name="value2">The second vector location.</param>
/// <returns>The distance between the two vectors.</returns>
public static float Distance(Vector2 value1, Vector2 value2)
{
float num2 = value1.X - value2.X;
float num = value1.Y - value2.Y;
float num3 = (num2 * num2) + (num * num);
return (float)Math.Sqrt(num3);
}
/// <summary>Checks to see if a location is in the spawn protection area.</summary>
/// <param name="x">The x coordinate to check.</param>
/// <param name="y">The y coordinate to check.</param>
/// <returns>If the given x,y location is in the spawn area.</returns>
public static bool IsInSpawn(int x, int y)
{
Vector2 tile = new Vector2(x, y);
Vector2 spawn = new Vector2(Main.spawnTileX, Main.spawnTileY);
return Distance(spawn, tile) <= TShock.Config.SpawnProtectionRadius;
}
/// <summary>Computes the max styles...</summary>
internal void ComputeMaxStyles()
{
var item = new Item();
for (int i = 0; i < Main.maxItemTypes; i++)
{
item.netDefaults(i);
if (item.placeStyle > 0)
{
if (GetDataHandlers.MaxPlaceStyles.ContainsKey(item.createTile))
{
if (item.placeStyle > GetDataHandlers.MaxPlaceStyles[item.createTile])
GetDataHandlers.MaxPlaceStyles[item.createTile] = item.placeStyle;
}
else
GetDataHandlers.MaxPlaceStyles.Add(item.createTile, item.placeStyle);
}
}
}
}
}

View file

@ -1,54 +0,0 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ClassLibrary1")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Multiplay")]
[assembly: AssemblyProduct("ClassLibrary1")]
[assembly: AssemblyCopyright("Copyright © Multiplay 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c6aed7ee-6282-49a2-8177-b79cad20d6d3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -1,210 +0,0 @@
/*
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.WebTesting;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;
using Rests;
namespace TshockRestTestPlugin
{
[DisplayName("JSON Status")]
[Description("Checks to see the that the JSON response has the specified status response")]
public class JsonValidateStatus : JsonValidate
{
public override void Validate(object sender, ValidationEventArgs e)
{
if (null != ValidateJson(sender, e))
e.IsValid = true;
}
}
[DisplayName("JSON Regexp Property")]
[Description("Checks to see the that the JSON response contains the specified property and is matches the specified regexp")]
public class JsonValidateRegexpProperty : JsonValidateProperty
{
// The name of the desired JSON property
[DisplayName("Regexp")]
[DefaultValue(true)]
public new bool UseRegularExpression { get { return base.UseRegularExpression; } set { base.UseRegularExpression = value; } }
}
[DisplayName("JSON Error")]
[Description("Checks to see the that the JSON response contains the specified error")]
public class JsonValidateError : JsonValidateProperty
{
// The status of the JSON request
[DisplayName("JSON Status")]
[DefaultValue("400")]
public new string JSonStatus { get { return base.JSonStatus; } set { base.JSonStatus = value; } }
// The name of the desired JSON property
[DisplayName("Property")]
[DefaultValue("error")]
public new string PropertyName { get { return base.PropertyName; } set { base.PropertyName = value; } }
}
[DisplayName("JSON Missing Parameter")]
[Description("Checks to see the that the JSON response indicates a missing or invalid parameter")]
public class JsonValidateMissingParameter : JsonValidateError
{
// The value of the desired JSON property
[DisplayName("Missing Value")]
public new string PropertyValue { get { return base.PropertyValue; } set { base.PropertyValue = String.Format("Missing or empty {0} parameter", value); } }
}
[DisplayName("JSON Invalid Parameter")]
[Description("Checks to see the that the JSON response indicates a missing or invalid parameter")]
public class JsonValidateInvalidParameter : JsonValidateError
{
// The value of the desired JSON property
[DisplayName("Invalid Value")]
public new string PropertyValue { get { return base.PropertyValue; } set { base.PropertyValue = String.Format("Missing or invalid {0} parameter", value); } }
}
[DisplayName("JSON Response")]
[Description("Checks to see the that the JSON response contains the specified message")]
public class JsonValidateResponse : JsonValidateProperty
{
// The name of the desired JSON property
[DisplayName("Response")]
[DefaultValue("response")]
public new string PropertyName { get { return base.PropertyName; } set { base.PropertyName = value; } }
}
[DisplayName("JSON Property")]
[Description("Checks to see the that the JSON response contains the specified property and is set to the specified value")]
public class JsonValidateProperty : JsonValidate
{
// The name of the desired JSON property
[DisplayName("Property")]
public string PropertyName { get; set; }
// The value of the desired JSON property
[DisplayName("Value")]
public string PropertyValue { get; set; }
// Is the value a regexp of the desired JSON property
[DisplayName("Regexp")]
[DefaultValue(false)]
public bool UseRegularExpression { get; set; }
public override void Validate(object sender, ValidationEventArgs e)
{
RestObject response = ValidateJson(sender, e);
if (null == response)
return;
if (null == response[PropertyName])
{
e.Message = String.Format("{0} Not Found", PropertyName);
e.IsValid = false;
return;
}
if (UseRegularExpression)
{
var re = new Regex(PropertyValue);
if (!re.IsMatch((string)response[PropertyName]))
{
e.Message = String.Format("{0} => '{1}' !~ '{2}'", PropertyName, response[PropertyName], PropertyValue);
e.IsValid = false;
return;
}
}
else
{
if (PropertyValue != (string)response[PropertyName])
{
e.Message = String.Format("{0} => '{1}' != '{2}'", PropertyName, response[PropertyName], PropertyValue);
e.IsValid = false;
return;
}
}
e.IsValid = true;
//e.WebTest.Context.Add(ContextParameterName, propertyValue);
}
}
[DisplayName("JSON Has Properties")]
[Description("Checks to see the that the JSON response contains the specified properties (comma seperated)")]
public class JsonHasProperties : JsonValidate
{
// The name of the desired JSON properties to check
[DisplayName("Properties")]
[Description("A comma seperated list of property names to check exist")]
public string PropertyNames { get; set; }
//---------------------------------------------------------------------
public override void Validate(object sender, ValidationEventArgs e)
{
RestObject response = ValidateJson(sender, e);
if (null == response)
return;
foreach (var p in PropertyNames.Split(','))
{
if (null == response[p])
{
e.Message = String.Format("'{0}' Not Found", p);
e.IsValid = false;
return;
}
}
e.IsValid = true;
//e.WebTest.Context.Add(ContextParameterName, propertyValue);
}
}
public abstract class JsonValidate : ValidationRule
{
// The status of the JSON request
[DisplayName("JSON Status")]
[DefaultValue("200")]
public string JSonStatus { get; set; }
public RestObject ValidateJson(object sender, ValidationEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.Response.BodyString))
{
e.IsValid = false;
e.Message = String.Format("Empty or null response {0}", e.Response.StatusCode);
return null;
}
JavaScriptSerializer serialiser = new JavaScriptSerializer();
//dynamic data = serialiser.Deserialize<dynamic>(e.Response.BodyString);
RestObject response = serialiser.Deserialize<RestObject>(e.Response.BodyString);
if (JSonStatus != response.Status)
{
e.IsValid = false;
e.Message = String.Format("Response Status '{0}' not '{1}'", response.Status, JSonStatus);
return null;
}
return response;
}
}
}

View file

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F2FEDAFB-58DE-4611-9168-A86112C346C7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TshockRestTestPlugin</RootNamespace>
<AssemblyName>TshockRestTestPlugin</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="TShockRestTestPlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TShockAPI\TShockAPI.csproj">
<Project>{49606449-072B-4CF5-8088-AA49DA586694}</Project>
<Name>TShockAPI</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -1,6 +1,6 @@
'''
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -145,7 +145,7 @@ def update_terraria_source():
def run_bootstrapper():
for build_config in ['Debug','Release'] :
mintaka = subprocess.Popen(['xbuild', './TerrariaServerAPI/TShock.4.OTAPI.sln', '/p:Configuration=' + build_config])
mintaka = subprocess.Popen(['msbuild', './TerrariaServerAPI/TShock.4.OTAPI.sln', '/p:Configuration=' + build_config])
mintaka.wait()
@ -161,7 +161,7 @@ def run_bootstrapper():
if (bootstrapper_proc.returncode != 0):
raise CalledProcessError(bootstrapper_proc.returncode)
tsapi_proc = subprocess.Popen(['xbuild', './TerrariaServerAPI/TerrariaServerAPI/TerrariaServerAPI.csproj', '/p:Configuration=' + build_config])
tsapi_proc = subprocess.Popen(['msbuild', './TerrariaServerAPI/TerrariaServerAPI/TerrariaServerAPI.csproj', '/p:Configuration=' + build_config])
tsapi_proc.wait()
@ -169,8 +169,8 @@ def run_bootstrapper():
raise CalledProcessError(tsapi_proc.returncode)
def build_software():
release_proc = subprocess.Popen(['xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Release'])
debug_proc = subprocess.Popen(['xbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Debug'])
release_proc = subprocess.Popen(['msbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Release'])
debug_proc = subprocess.Popen(['msbuild', './TShockAPI/TShockAPI.csproj', '/p:Configuration=Debug'])
release_proc.wait()
debug_proc.wait()
if (release_proc.returncode != 0):
@ -182,7 +182,7 @@ def build_software():
if __name__ == '__main__':
create_release_folder()
update_terraria_source()
run_bootstrapper();
run_bootstrapper()
copy_dependencies()
build_software()
package_release()

View file

@ -1,6 +1,6 @@
'''
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

@ -1,5 +1,5 @@
''' TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -23,11 +23,11 @@ extensions = {'.cs', '.py'}
path = "./"
pattern = "/\*\s?\n?TShock, a server mod for Terraria(\n|.)*\*/"
pypattern = "'''\s?\n?TShock, a server mod for Terraria(\n|.)*'''"
year = "2016"
year = "2019"
filename = "./README.md"
text = "/*\n\
TShock, a server mod for Terraria\n\
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)\n\
Copyright (C) 2011-2019 Pryaxis & TShock Contributors\n\
\n\
This program is free software: you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\

View file

@ -1,6 +1,6 @@
'''
TShock, a server mod for Terraria
Copyright (C) 2011-2016 Nyx Studios (fka. The TShock Team)
Copyright (C) 2011-2019 Pryaxis & TShock Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by