๐ณ๏ธ HytaleVotifier
Reward your players for supporting your server! ๐
A Votifier-style plugin for Hytale that receives vote notifications from voting websites via HTTP and fires events for other plugins to handle rewards.
โจ Features
| Feature | Description |
|---|---|
| ๐ Dual Protocol Support | V1 (RSA encryption) and V2 (HMAC-SHA256 signatures) for maximum compatibility |
| ๐ V2 Socket Server | Dedicated TCP socket server with challenge-response authentication |
| ๐ HTTP Endpoints | REST API for receiving votes (auto-detects V1/V2) and checking server status |
| ๐ก Event System | Fires VoteEvent via Hytale's event bus for other plugins to handle rewards |
| ๐ก๏ธ Secure Protocols | RSA/ECB/PKCS1Padding (V1) and HMAC-SHA256 with per-site tokens (V2) |
| ๐ฐ Reward Commands | Execute configurable server commands with random chance when votes are received |
| ๐ข Vote Broadcasting | Announce votes to all online players with customizable messages |
| ๐ Toast Notifications | Display in-game toast popups to voters using TaleMessage formatting |
| ๐ Update Checker | Automatic GitHub release checking with admin notifications |
| ๐งช Debug Tools | /testvote command for development and troubleshooting |
| ๐ณ๏ธ Vote Command | /vote command displays clickable voting site links to players |
| โฐ Vote Reminders | Remind players to vote when they join if they haven't voted recently |
| ๐พ Vote Storage | Persistent SQLite storage tracks when players last voted |
๐ Requirements
- โ Java 25 or higher
- ๐ฎ Hytale Server with plugin support
- ๐ Nitrado:WebServer plugin โ Optional; if unavailable, a built-in fallback HTTP server is used
๐ Installation
-
Build the plugin (if building from source):
mvn clean package -
Copy the JAR to your server's
mods/directory:mods/ โโโ HytaleVotifier-1.2.0.jar -
(Optional) Install Nitrado:WebServer โ If available, HytaleVotifier uses it for HTTP handling. Otherwise, a built-in fallback HTTP server is automatically started on port 8080
-
Start the server โ RSA keys will be automatically generated on first run ๐
-
Verify installation by accessing the status endpoint:
GET http://your-server:port/Hyvote/HytaleVotifier/status
โ๏ธ Configuration
Configuration is stored in config.json in the plugin's data directory. The file is created automatically on first run with default values.
๐ Configuration File Location
mods/Hyvote_HytaleVotifier/
โโโ config.json # Plugin configuration
โโโ votes.db # Vote tracking database (SQLite)
โโโ keys/
โโโ public.key # ๐ค Share with voting sites
โโโ private.key # ๐ Keep secure - never share!
๐ Full Configuration Example
{
"debug": false,
"keyPath": "keys",
"voteMessage": {
"enabled": false,
"titleMessage": "<orange>Vote Received!</orange>",
"descriptionMessage": "<gray>Thanks for your vote on <orange>{from}</orange>!</gray>",
"iconItem": "Ore_Gold"
},
"broadcast": {
"enabled": false,
"message": "<orange>{username}</orange> <gray>voted on</gray> <orange>{from}</orange><gray>!</gray>"
},
"rewardCommands": [
{
"enabled": false,
"command": "give {username} Ingredient_Stick",
"chance": 1.0
},
{
"enabled": false,
"command": "give {username} Ingredient_Bar_Iron",
"chance": 0.1
}
],
"voteSites": {
"tokens": {
"Hyvote": "your-secret-token-here",
"MyVotingSite": "another-secret-token"
}
},
"socketServer": {
"enabled": true,
"port": 8192
},
"internalHttpServer": {
"enabled": true,
"port": 8080
},
"protocols": {
"v1Enabled": true,
"v2Enabled": true
},
"voteCommand": {
"enabled": false,
"header": "<red>================<orange> Vote Now </orange>================</red>",
"siteTemplate": "<orange><click:{link}>~{name}~</click></orange>",
"footer": "<red>==============<orange> Earn Rewards </orange>==============</red>",
"sites": [
{
"name": "Hyvote.org",
"url": "https://hyvote.org"
}
]
},
"voteReminder": {
"enabled": true,
"sendOnJoin": true,
"voteExpiryInterval": 24,
"delayInSeconds": 60,
"storage": {
"type": "sqlite",
"filePath": "votes.db",
"cleanupIntervalHours": 6
},
"message": {
"enabled": true,
"text": "<gray>You haven't voted today! You can <orange>'/vote'</orange> every day to receive free rewards!</gray>"
},
"title": {
"enabled": true,
"title": "You can /vote every day for free rewards!",
"subTitle": "You haven't voted today",
"durationSeconds": 3.0,
"fadeInSeconds": 0.5,
"fadeOutSeconds": 0.5
},
"notification": {
"enabled": true,
"titleMessage": "<orange>You haven't voted today</orange>",
"descriptionMessage": "<gray>You can <orange>/vote</orange> every day for free rewards!</gray>",
"iconItem": "Upgrade_Backpack_2"
},
"sound": {
"enabled": true,
"sound": "SFX_Avatar_Powers_Enable",
"soundCategory": "UI"
}
}
}
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
debug |
boolean | false |
Enable verbose debug logging |
keyPath |
string | "keys" |
Subdirectory for RSA keys (relative to plugin data directory) |
voteMessage |
object | โ | Toast notification settings (see below) |
broadcast |
object | โ | Server-wide broadcast settings (see below) |
rewardCommands |
array | โ | Commands to execute on vote (see below) |
voteSites |
object | โ | V2 protocol service tokens (see V2 Configuration) |
socketServer |
object | โ | V2 socket server settings (see V2 Configuration) |
internalHttpServer |
object | โ | Fallback HTTP server settings (see below) |
protocols |
object | โ | Protocol enable/disable settings (see below) |
voteCommand |
object | โ | /vote command settings (see below) |
voteReminder |
object | โ | Vote reminder settings (see below) |
๐ Vote Message (Toast Notifications)
Display a toast popup to the player who voted.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable toast notifications |
titleMessage |
string | "<orange>Vote Received!</orange>" |
Toast title with TaleMessage formatting |
descriptionMessage |
string | "<gray>Thanks for your vote on <orange>{from}</orange>!</gray>" |
Toast description with placeholders |
iconItem |
string | "Ore_Gold" |
Item ID to display as the toast icon |
๐ข Broadcast Settings
Announce votes to all online players.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable server-wide vote broadcasts |
message |
string | "<orange>{username}</orange> <gray>voted on</gray> <orange>{from}</orange><gray>!</gray>" |
Broadcast message with TaleMessage formatting and placeholders |
๐ฐ Reward Commands
Execute server commands when votes are received. Each command in the array can have its own probability โ perfect for tiered rewards!
| Option | Type | Description |
|---|---|---|
enabled |
boolean | Whether this reward is active (allows disabling without removing) |
command |
string | Command to execute (without leading /). Supports placeholders. |
chance |
number | Probability of execution (0.0 to 1.0). Use 1.0 for guaranteed execution. |
Example reward configuration:
"rewardCommands": [
{
"enabled": true,
"command": "give {username} Ingredient_Stick",
"chance": 1.0
},
{
"enabled": true,
"command": "give {username} Ingredient_Bar_Gold",
"chance": 0.25
},
{
"enabled": true,
"command": "give {username} Weapon_Longsword_Adamantite_Saurian",
"chance": 0.05
}
]
โ๏ธ In this example, every voter receives a stick, has a 25% chance for a gold bar, and a 5% chance for a rare longsword! โ๏ธ
โ ๏ธ Security Note: Usernames and service names are validated before command execution to prevent command injection. Only alphanumeric characters and underscores are allowed in usernames.
๐ Fallback HTTP Server
When the Nitrado:WebServer plugin is not available, HytaleVotifier automatically starts its own HTTP server using Java's built-in HttpServer.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable the fallback HTTP server when Nitrado:WebServer is unavailable |
port |
number | 8080 |
The port to listen on for HTTP requests |
๐ก Note: The fallback HTTP server provides the same
/Hyvote/HytaleVotifier/voteand/Hyvote/HytaleVotifier/statusendpoints as when using Nitrado:WebServer. If Nitrado:WebServer is installed, the fallback server is not started.
โ ๏ธ Important: The HTTP server (both Nitrado:WebServer and fallback) is only started when V1 protocol is enabled. If you only use V2 protocol via the socket server, you can disable V1 to skip HTTP server initialization entirely.
๐ง Protocol Settings
Control which vote protocols are enabled. Both protocols are enabled by default.
| Option | Type | Default | Description |
|---|---|---|---|
v1Enabled |
boolean | true |
Enable V1 protocol (RSA-encrypted votes over HTTP) |
v2Enabled |
boolean | true |
Enable V2 protocol (HMAC-SHA256 signed votes over HTTP or socket) |
๐ก Note: V1 protocol requires an HTTP server. If V1 is disabled, the HTTP server will not be started (even if available), and votes can only be received via the V2 socket server.
๐ก Note: V2 protocol can work over both HTTP and socket. If you disable V1 but keep V2 enabled, configure the socket server to receive V2 votes.
๐ณ๏ธ Vote Command Settings
Display clickable voting site links to players with /vote.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable the /vote command |
header |
string | "<red>================<orange> Vote Now </orange>================</red>" |
Header message displayed before sites |
siteTemplate |
string | "<orange><click:{link}>~{name}~</click></orange>" |
Template for each site with {name} and {link} placeholders |
footer |
string | "<red>==============<orange> Earn Rewards </orange>==============</red>" |
Footer message displayed after sites |
sites |
array | โ | List of voting sites (displayed in order) |
Site object properties:
| Property | Type | Description |
|---|---|---|
name |
string | Display name of the voting site |
url |
string | URL where players can vote |
Example vote command configuration:
"voteCommand": {
"enabled": true,
"header": "<gold>โ
โ
โ
Vote for our server! โ
โ
โ
</gold>",
"siteTemplate": "<yellow>โค</yellow> <click:{link}><aqua>{name}</aqua></click>",
"footer": "<gray>Thank you for supporting us!</gray>",
"sites": [
{
"name": "Hyvote.org",
"url": "https://hyvote.org/servers/my-server"
},
{
"name": "MyVotingSite",
"url": "https://example.com/vote/my-server"
}
]
}
โฐ Vote Reminder Settings
Remind players to vote when they join the server if they haven't voted recently. The reminder can include a chat message, title display, toast notification, and sound.
Main Settings
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable the vote reminder system |
sendOnJoin |
boolean | true |
Send reminders when players join the server |
voteExpiryInterval |
number | 24 |
Hours before a vote "expires" and reminders resume |
delayInSeconds |
number | 15 |
Delay (in seconds) after joining before sending the reminder |
Storage Settings
Vote timestamps are stored to track when players last voted. The storage is also used for periodic cleanup of expired records.
| Option | Type | Default | Description |
|---|---|---|---|
storage.type |
string | "sqlite" |
Storage backend: "sqlite" (persistent) or "memory" (clears on restart) |
storage.filePath |
string | "votes.db" |
Database file path relative to plugin data directory |
storage.cleanupIntervalHours |
number | 6 |
How often to run cleanup of expired vote records |
๐ก Note: The cleanup task removes vote records older than
voteExpiryIntervalto keep the database file size reasonable. Cleanup runs immediately on server startup and then at the configured interval.
Message Settings
Send a direct chat message to the player.
| Option | Type | Default | Description |
|---|---|---|---|
message.enabled |
boolean | true |
Enable the chat message reminder |
message.text |
string | (see below) | Message text with TaleMessage formatting |
Default message:
<gray>You haven't voted today! You can <orange>'/vote'</orange> every day to receive free rewards!</gray>
Title Settings
Display a title at the center of the screen.
| Option | Type | Default | Description |
|---|---|---|---|
title.enabled |
boolean | true |
Enable the title display |
title.title |
string | "You can /vote every day for free rewards!" |
Main title text |
title.subTitle |
string | "You haven't voted today" |
Subtitle text below the main title |
title.durationSeconds |
number | 3.0 |
How long the title is displayed |
title.fadeInSeconds |
number | 0.5 |
Fade-in animation duration |
title.fadeOutSeconds |
number | 0.5 |
Fade-out animation duration |
Notification Settings
Display a toast notification popup.
| Option | Type | Default | Description |
|---|---|---|---|
notification.enabled |
boolean | true |
Enable the toast notification |
notification.titleMessage |
string | "<orange>You haven't voted today</orange>" |
Toast title with TaleMessage formatting |
notification.descriptionMessage |
string | "<gray>You can <orange>/vote</orange> every day for free rewards!</gray>" |
Toast description |
notification.iconItem |
string | "Upgrade_Backpack_2" |
Item ID to display as the toast icon |
Sound Settings
Play a sound with the reminder.
| Option | Type | Default | Description |
|---|---|---|---|
sound.enabled |
boolean | true |
Enable the reminder sound |
sound.sound |
string | "SFX_Avatar_Powers_Enable" |
Sound ID to play |
sound.soundCategory |
string | "UI" |
Sound category for volume control ("UI", "MUSIC", "SFX") |
Example vote reminder configuration:
"voteReminder": {
"enabled": true,
"sendOnJoin": true,
"voteExpiryInterval": 24,
"delayInSeconds": 120,
"storage": {
"type": "sqlite",
"filePath": "votes.db",
"cleanupIntervalHours": 12
},
"message": {
"enabled": true,
"text": "<gold>Hey!</gold> <gray>Support us by voting with</gray> <green>/vote</green>"
},
"title": {
"enabled": false
},
"notification": {
"enabled": true,
"titleMessage": "<gold>Vote for Rewards!</gold>",
"descriptionMessage": "<gray>Type /vote to earn free items!</gray>",
"iconItem": "Ore_Gold"
},
"sound": {
"enabled": true,
"sound": "SFX_UI_Quest_Complete",
"soundCategory": "UI"
}
}
๐ท๏ธ Available Placeholders
The following placeholders can be used in messages and commands:
| Placeholder | Description |
|---|---|
{username} |
The in-game username of the player who voted |
{from} |
The name of the voting site (service name) |
๐จ TaleMessage Formatting
Vote messages and broadcasts support TaleMessage formatting tags:
<red>Red text</red>
<orange>Orange text</orange>
<yellow>Yellow text</yellow>
<green>Green text</green>
<blue>Blue text</blue>
<gray>Gray text</gray>
<bold>Bold text</bold>
<italic>Italic text</italic>
<click:https://example.com>Clickable text</click>
๐ HTTP API Reference
All endpoints are mounted at /Hyvote/HytaleVotifier/.
GET /status
Health check endpoint that returns server status information.
Response:
{
"status": "ok",
"version": "1.2.0",
"serverType": "HytaleVotifier",
"protocols": {
"v1": true,
"v2": true
}
}
Status Codes:
- โ
200 OKโ Server is running and keys are initialized - โ
503 Service Unavailableโ RSA keys not initialized
POST /vote
Receives vote notifications from voting sites. The endpoint auto-detects the protocol (V1 or V2) based on the payload format.
V1 Request (RSA Encrypted)
Content-Type: text/plain or application/octet-stream
Request Body: Base64-encoded RSA-encrypted vote payload
V2 Request (HMAC Signed)
Content-Type: application/json
Request Body:
{
"payload": "{\"serviceName\":\"Hyvote\",\"username\":\"PlayerName\",\"address\":\"192.168.1.1\",\"timestamp\":1704067200}",
"signature": "BASE64_HMAC_SHA256_SIGNATURE"
}
Response (Success)
{
"status": "ok",
"message": "Vote processed for PlayerName"
}
Response (Error)
{
"status": "error",
"message": "Error description"
}
Status Codes:
- โ
200 OKโ Vote received and processed successfully - โ ๏ธ
400 Bad Requestโ Empty payload, invalid format, decryption/signature failed, or invalid vote data - โ
500 Internal Server Errorโ Unexpected server error
๐ Vote Protocols
HytaleVotifier supports two protocols for receiving votes from voting sites:
| Protocol | Authentication | Transport | Use Case |
|---|---|---|---|
| V1 | RSA 2048-bit encryption | HTTP POST | Classic Votifier compatibility |
| V2 | HMAC-SHA256 signatures | HTTP POST or TCP socket | Modern NuVotifier compatibility |
๐ V1 Protocol (RSA)
The original Votifier protocol using RSA public-key encryption.
Payload Format
The vote data is a newline-delimited string:
VOTE
serviceName
username
address
timestamp
Encryption Flow
1. ๐ Voting site retrieves server's public key
2. ๐ Voting site encrypts vote payload using RSA/ECB/PKCS1Padding
3. ๐ฆ Voting site Base64-encodes the encrypted bytes
4. ๐ค Voting site POSTs the Base64 string to /vote endpoint
5. ๐ฅ Server decodes Base64 and decrypts with private key
6. โ
Server parses and validates vote data
7. ๐ก Server fires VoteEvent for listening plugins
๐ V2 Protocol (HMAC-SHA256)
The NuVotifier V2 protocol uses HMAC-SHA256 signatures with per-service shared secret tokens instead of RSA encryption. This provides easier key management and works over both HTTP and dedicated socket connections.
Key Differences from V1
| Aspect | V1 | V2 |
|---|---|---|
| Authentication | Single RSA key pair for all sites | Unique token per voting site |
| Setup | Share public key with sites | Exchange shared secret tokens |
| Transport | HTTP only | HTTP or TCP socket |
| Replay Protection | None | Challenge-response (socket mode) |
V2 Payload Format
The V2 protocol uses JSON with a signed inner payload:
{
"payload": "{\"serviceName\":\"Hyvote\",\"username\":\"PlayerName\",\"address\":\"192.168.1.1\",\"timestamp\":1704067200,\"challenge\":\"abc123...\"}",
"signature": "BASE64_HMAC_SHA256_SIGNATURE"
}
| Field | Description |
|---|---|
payload |
Stringified JSON containing the vote data |
signature |
Base64-encoded HMAC-SHA256 of the payload string using the service's token |
Inner Payload Fields
| Field | Type | Required | Description |
|---|---|---|---|
serviceName |
string | โ | Identifier of the voting site (must match config) |
username |
string | โ | In-game username of the player who voted |
address |
string | โ | IP address of the voter |
timestamp |
number | โ | Unix timestamp (seconds or milliseconds) |
challenge |
string | Socket only | Challenge token from server greeting |
V2 HTTP Mode
Send V2 votes to the same HTTP endpoint as V1 votes. The server auto-detects the protocol based on payload format.
POST /Hyvote/HytaleVotifier/vote
Content-Type: application/json
{
"payload": "{\"serviceName\":\"...\",\"username\":\"...\",\"timestamp\":...}",
"signature": "..."
}
V2 Socket Mode
The socket server provides additional security through challenge-response authentication, preventing replay attacks.
Default Port: 8192
Protocol Flow:
1. ๐ก Client connects to socket server
2. ๐ค Server sends: "VOTIFIER 2 <challenge>\n"
3. ๐ฅ Client sends binary packet:
- Magic bytes: 0x733A (2 bytes, big-endian)
- Length: payload length (2 bytes, big-endian)
- Payload: JSON wrapper with challenge included
4. โ
Server verifies signature AND challenge
5. ๐ค Server responds with JSON result
Server Response:
// Success
{"status":"ok","cause":null,"errorMessage":null}
// Error
{"status":"error","cause":"error description","errorMessage":"error description"}
๐ง V2 Configuration
To use V2 protocol, configure tokens for each voting site in config.json:
{
"voteSites": {
"tokens": {
"Hyvote": "your-secret-token-from-voting-site",
"MyVotingSite": "another-secret-token"
}
},
"socketServer": {
"enabled": true,
"port": 8192
},
"protocols": {
"v1Enabled": true,
"v2Enabled": true
}
}
Vote Site Token Configuration
| Option | Type | Default | Description |
|---|---|---|---|
voteSites.tokens |
object | {} |
Map of service names to their shared secret tokens |
๐ Important: Service name lookups are case-insensitive. The service name "Hyvote" will match "hyvote", "HYVOTE", etc.
Socket Server Configuration
| Option | Type | Default | Description |
|---|---|---|---|
socketServer.enabled |
boolean | true |
Enable the V2 TCP socket server |
socketServer.port |
number | 8192 |
Port for the socket server to listen on |
๐ก Note: The socket server is only started when V2 protocol is enabled in the
protocolsconfig and at least one vote site token is configured.
Setting Up V2 with a Voting Site
- Get your token from the voting site's server configuration panel
- Add the token to your
config.jsonundervoteSites.tokenswith the exact service name - Configure the voting site with your server's socket address and port (for socket mode) or HTTP endpoint (for HTTP mode)
- Restart your server to apply the configuration
Vote Record Fields
| Field | Type | Description |
|---|---|---|
serviceName |
String | Identifier of the voting site (e.g., "Hyvote") |
username |
String | In-game username of the player who voted |
address |
String | IP address of the voter (as reported by voting site) |
timestamp |
long | Epoch milliseconds when the vote was cast |
๐ Plugin Integration
Want to build your own reward system? Other plugins can listen for vote events to handle rewards.
Registering a Vote Listener
import org.hyvote.plugins.votifier.event.VoteEvent;
import org.hyvote.plugins.votifier.vote.Vote;
public class MyRewardPlugin extends JavaPlugin {
@Override
protected void setup() {
// Register vote event listener
getEventRegistry().register(VoteEvent.class, this::onVote);
}
private void onVote(VoteEvent event) {
Vote vote = event.getVote();
String username = vote.username();
String service = vote.serviceName();
String address = vote.address();
long timestamp = vote.timestamp();
// Handle vote reward
getLogger().info("Player " + username + " voted on " + service);
// Example: Give reward to player
// Player player = getServer().getPlayer(username);
// if (player != null) {
// giveReward(player);
// } else {
// // Store for later - player is offline
// storeOfflineReward(username);
// }
}
}
Event Details
- ๐ฆ Event Class:
org.hyvote.plugins.votifier.event.VoteEvent - โก Fires: When a valid vote is received (encrypted or via test endpoint)
- ๐ค Offline Players: Events fire regardless of player online status โ your plugin should handle offline scenarios
VoteEvent Convenience Methods
event.getVote() // Returns the Vote record
event.getServiceName() // Shortcut for vote.serviceName()
event.getUsername() // Shortcut for vote.username()
event.getAddress() // Shortcut for vote.address()
event.getTimestamp() // Shortcut for vote.timestamp()
๐ Update Checker
HytaleVotifier automatically checks for updates on GitHub when the server starts. Never miss a new feature! ๐
How It Works
- ๐ On server startup, the plugin queries the GitHub API for the latest release
- ๐ If a newer version is available, a message is logged to the console
- ๐ค When players with admin permissions join, they receive a clickable notification with download links
Console Output
When an update is available, the console displays:
[Votifier] A new update is available: v1.2.0
[Votifier] Download from CurseForge: https://www.curseforge.com/hytale/mods/votifier
[Votifier] Download from GitHub: https://github.com/Hyvote/hytale-votifier/releases/latest
Player Notifications
Players with the appropriate permissions see a chat message with clickable links to download the update from CurseForge or GitHub.
๐ Permissions
| Permission | Description |
|---|---|
votifier.command.vote |
Use the /vote command (granted by default to all game mode groups; can be negated per user/group) |
votifier.admin.testvote |
Use the /testvote command to fire test vote events |
votifier.admin |
Receive update notifications when joining the server |
votifier.admin.update_notifications |
Alternative permission for update notifications only |
๐งช Testing
/testvote Command
In-game command for testing vote events:
/testvote <username> [service]
| Argument | Required | Default | Description |
|---|---|---|---|
username |
Yes | โ | The player username for the test vote |
service |
No | TestService |
The voting site name to simulate |
Permission Required: votifier.admin.testvote
This command fires a VoteEvent as if the specified player had voted, triggering all configured features (toast notifications, broadcasts, and reward commands). Perfect for testing your reward logic without external voting sites! ๐ฏ
๐ License
MIT License
Copyright (c) 2026 Hyvote
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.






